From b9295a8fb2cdefde724a618bff335c67a2e84b7a Mon Sep 17 00:00:00 2001 From: Ben U Date: Wed, 29 Jul 2026 17:55:38 -0600 Subject: [PATCH 1/2] feat: add dormant Boss adapter contracts --- .gitignore | 1 - README.md | 17 + boss-control-outbox.test.ts | 74 ++ boss-control-outbox.ts | 195 ++++ broker/authorization.test.ts | 66 ++ broker/authorization.ts | 21 +- broker/boss-adapter.test.ts | 260 +++++ broker/boss-adapter.ts | 531 +++++++++ broker/boss-control-ledger.test.ts | 233 ++++ broker/boss-control-ledger.ts | 306 ++++++ broker/broker.ts | 390 ++++++- broker/client.test.ts | 206 ++++ broker/client.ts | 277 ++++- codex/app-server-client.test.ts | 30 + codex/app-server-client.ts | 11 +- codex/boss-client.test.ts | 30 + codex/boss-client.ts | 34 + codex/bridge-config.test.ts | 120 +- codex/bridge-config.ts | 174 ++- codex/bridge-daemon.test.ts | 30 +- codex/bridge-daemon.ts | 65 +- codex/coi.test.ts | 190 +++- codex/coi.ts | 184 +++- codex/team.test.ts | 309 +++++- codex/team.ts | 237 +++- dist/bridge-daemon.mjs | 1221 +++++++++++++++++---- dist/broker.mjs | 1089 +++++++++++++----- dist/build-info.json | 2 +- dist/codex-server.mjs | 1018 +++++++++++++---- dist/coi.mjs | 1398 +++++++++++++++++++----- package-lock.json | 614 +++++++++++ package.json | 11 +- scripts/boss-remediation-dist.test.mjs | 172 +++ scripts/build-info.mjs | 1 + scripts/build-info.test.mjs | 2 +- scripts/build.mjs | 8 +- types.ts | 65 +- 37 files changed, 8547 insertions(+), 1045 deletions(-) create mode 100644 boss-control-outbox.test.ts create mode 100644 boss-control-outbox.ts create mode 100644 broker/boss-adapter.test.ts create mode 100644 broker/boss-adapter.ts create mode 100644 broker/boss-control-ledger.test.ts create mode 100644 broker/boss-control-ledger.ts create mode 100644 codex/boss-client.test.ts create mode 100644 codex/boss-client.ts create mode 100644 package-lock.json create mode 100644 scripts/boss-remediation-dist.test.mjs diff --git a/.gitignore b/.gitignore index 51e9c9c..46a9df7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ dist/* !dist/build-info.json *.log .DS_Store -package-lock.json progress.md diff --git a/README.md b/README.md index 4319b8e..5d24748 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,23 @@ appear in the already-open terminal instead of existing only in the saved transcript. This refresh happens after the turn is idle so Codex does not reopen in a phantom `Working` state. Retryable app-server stream errors are reported as reconnecting without terminating the sidecar, allowing Codex's own retry to complete. Orchestrated `fresh: true` launches remove the saved `coi` thread state before registration. +The additive Stage-B `boss-run-v1` adapter contracts are present but dormant. +Ordinary local and remote-access communication continues to use the existing +protocol-v3 behavior when Boss metadata is omitted. The legacy broker does not +advertise or bind Boss participants until a protected provider supplies all of +the required broker identity, credential-registry, authority-transition, and +participant-health predicates. Boss-scoped discovery and routing fail closed +across ordinary sessions and other Boss runs. The `boss_participant` and +`boss_reviewer` launch-profile markers make production Codex launches fail +with `PROVIDER_AUTHORITY_UNAVAILABLE`: this adapter has no broker-owned, +artifact-attested provider executable and therefore never resolves protected +launches through caller `PATH`. Their non-spawning parser validators still +reject disabled approvals and `danger-full-access`, and reviewers remain +read-only. The markers do not install or advertise a restricted operation client. +Restricted Boss operation clients are not advertised or exported yet. They +remain unavailable until the protected broker can provision both the binding +and transport through a non-caller-forgeable factory. + ## Install For normal use, install the package so the command-line entry points are on diff --git a/boss-control-outbox.test.ts b/boss-control-outbox.test.ts new file mode 100644 index 0000000..10f6db9 --- /dev/null +++ b/boss-control-outbox.test.ts @@ -0,0 +1,74 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, readdirSync, rmSync, writeFileSync, mkdirSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { createHash } from "node:crypto"; +import test from "node:test"; +import { BOSS_CONTROL_ENVELOPE_VERSION } from "@dataforxyz/agent-intercom-core/boss"; +import { PersistentBossControlOutbox } from "./boss-control-outbox.ts"; + +const envelope = { + type: "boss.worker.health" as const, + version: BOSS_CONTROL_ENVELOPE_VERSION, + messageId: "message-1", + bossRunId: "run-1", + participantId: "worker-1", + bindingEpoch: 1, + idempotencyKey: "operation-1", + payload: { state: "working" }, +}; + +test("stable idempotency with a new messageId keeps one canonical request and the new caller correlation", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-outbox-")); + try { + const outbox = new PersistentBossControlOutbox("session-1", dir); + assert.equal(outbox.enqueue("target-session", envelope), "added"); + assert.equal(outbox.enqueue("target-session", { + payload: { state: "working" }, + idempotencyKey: "operation-1", + bindingEpoch: 1, + participantId: "worker-1", + bossRunId: "run-1", + messageId: "message-2", + version: BOSS_CONTROL_ENVELOPE_VERSION, + type: "boss.worker.health", + }), "existing"); + assert.equal(outbox.list().length, 1); + assert.equal(outbox.list()[0]?.envelope.messageId, "message-2"); + assert.throws(() => outbox.enqueue("other-session", envelope), /different canonical request/); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("result-order probes require durable ACK and exact deliveryId before outbox removal", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-outbox-order-")); + try { + const outbox = new PersistentBossControlOutbox("session-1", dir); + outbox.enqueue("target-session", envelope); + assert.throws(() => outbox.removeCorrelated("operation-1", "message-1", "delivery-1"), /before the matching durable acknowledgement/); + assert.equal(outbox.markAccepted("operation-1", "message-1", "delivery-1"), "accepted"); + assert.equal(new PersistentBossControlOutbox("session-1", dir).find("operation-1")?.state, "accepted"); + assert.equal(outbox.markAccepted("operation-1", "message-1", "delivery-1"), "already-accepted"); + assert.throws(() => outbox.removeCorrelated("operation-1", "message-1", "delivery-2"), /before the matching durable acknowledgement/); + assert.throws(() => outbox.removeCorrelated("operation-1", "message-1"), /omitted the durable deliveryId/); + outbox.removeCorrelated("operation-1", "message-1", "delivery-1"); + assert.deepEqual(new PersistentBossControlOutbox("session-1", dir).list(), []); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("corrupt Boss outbox is quarantined and fails closed", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-outbox-corrupt-")); + try { + const outboxDir = join(dir, "boss-control-outbox"); + mkdirSync(outboxDir, { recursive: true }); + const file = `${createHash("sha256").update("session-1").digest("hex")}.json`; + writeFileSync(join(outboxDir, file), "{not-json"); + assert.throws(() => new PersistentBossControlOutbox("session-1", dir), /corrupt and quarantined/); + assert.match(readdirSync(outboxDir)[0] ?? "", new RegExp(`^${file}\\.corrupt-`)); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/boss-control-outbox.ts b/boss-control-outbox.ts new file mode 100644 index 0000000..625a6d7 --- /dev/null +++ b/boss-control-outbox.ts @@ -0,0 +1,195 @@ +import { createHash } from "node:crypto"; +import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync } from "node:fs"; +import { join } from "node:path"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; +import { parseBossControlEnvelope, type BossControlEnvelope } from "@dataforxyz/agent-intercom-core/boss"; +import { assertBossCanonicalData } from "./broker/boss-adapter.ts"; +import { ensureIntercomRuntimeDir, getIntercomDirPath, INTERCOM_DIR_MODE, restrictIntercomRuntimeFile } from "./broker/paths.ts"; +import { writeDurableJson } from "./durable-json.ts"; + +const BOSS_CONTROL_OUTBOX_VERSION = 2; +const MAX_BOSS_CONTROL_OUTBOX_ENTRIES = 256; + +export interface StoredBossControl { + to: string; + envelope: BossControlEnvelope; + scope: string; + fingerprint: string; + queuedAt: number; + state: "queued" | "accepted"; + deliveryId?: string; +} + +interface BossControlOutboxState { + version: typeof BOSS_CONTROL_OUTBOX_VERSION; + entries: StoredBossControl[]; +} + +function scope(envelope: BossControlEnvelope): string { + return canonicalHash("agent-intercom-codex/boss-control/outbox-scope/v1", { + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey, + }); +} + +function fingerprint(to: string, envelope: BossControlEnvelope): string { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/outbox-request/v1", { to, envelope: stableEnvelope }); +} + +function exactKeys(value: Record, required: readonly string[], optional: readonly string[] = []): boolean { + const permitted = new Set([...required, ...optional]); + const keys = Reflect.ownKeys(value); + return required.every((key) => Object.hasOwn(value, key)) + && keys.every((key) => typeof key === "string" && permitted.has(key)); +} + +function parseEntry(value: unknown): StoredBossControl { + assertBossCanonicalData(value, "$.bossControlOutbox.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss outbox entry"); + const entry = value as Record; + if (!exactKeys(entry, ["to", "envelope", "scope", "fingerprint", "queuedAt", "state"], ["deliveryId"])) { + throw new Error("Invalid Boss outbox entry fields"); + } + if ( + typeof entry.to !== "string" + || entry.to.length === 0 + || typeof entry.scope !== "string" + || !/^[a-f0-9]{64}$/.test(entry.scope) + || typeof entry.fingerprint !== "string" + || !/^[a-f0-9]{64}$/.test(entry.fingerprint) + || typeof entry.queuedAt !== "number" + || !Number.isSafeInteger(entry.queuedAt) + || (entry.state !== "queued" && entry.state !== "accepted") + || (entry.state === "queued" && Object.hasOwn(entry, "deliveryId")) + || (entry.state === "accepted" && (!Object.hasOwn(entry, "deliveryId") || typeof entry.deliveryId !== "string" || entry.deliveryId.length === 0)) + ) throw new Error("Invalid Boss outbox entry binding"); + const envelope = parseBossControlEnvelope(entry.envelope); + if (entry.scope !== scope(envelope) || entry.fingerprint !== fingerprint(entry.to, envelope)) { + throw new Error("Boss outbox entry canonical binding mismatch"); + } + return { + to: entry.to, + envelope, + scope: entry.scope, + fingerprint: entry.fingerprint, + queuedAt: entry.queuedAt, + state: entry.state, + ...(entry.deliveryId === undefined ? {} : { deliveryId: entry.deliveryId as string }), + }; +} + +function fileName(sessionId: string): string { + return `${createHash("sha256").update(sessionId).digest("hex")}.json`; +} + +export class PersistentBossControlOutbox { + private readonly path: string; + private state: BossControlOutboxState; + + constructor(sessionId: string, intercomDir: string = getIntercomDirPath()) { + ensureIntercomRuntimeDir(intercomDir); + const directory = join(intercomDir, "boss-control-outbox"); + mkdirSync(directory, { recursive: true, mode: INTERCOM_DIR_MODE }); + if (process.platform !== "win32") chmodSync(directory, INTERCOM_DIR_MODE); + this.path = join(directory, fileName(sessionId)); + this.state = this.load(); + } + + list(): StoredBossControl[] { + return structuredClone(this.state.entries); + } + + find(idempotencyKey: string): StoredBossControl | undefined { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + return entry === undefined ? undefined : structuredClone(entry); + } + + enqueue(to: string, envelopeValue: unknown): "added" | "existing" { + if (typeof to !== "string" || to.length === 0) throw new Error("Boss target session ID is required"); + assertBossCanonicalData(envelopeValue, "$.envelope"); + const envelope = parseBossControlEnvelope(envelopeValue); + const candidateScope = scope(envelope); + const candidateFingerprint = fingerprint(to, envelope); + const existing = this.state.entries.find((entry) => entry.scope === candidateScope); + if (existing) { + if (existing.fingerprint !== candidateFingerprint) { + throw new Error(`Boss idempotency key ${envelope.idempotencyKey} is queued with a different canonical request`); + } + if (existing.envelope.messageId !== envelope.messageId) { + existing.envelope = envelope; + existing.queuedAt = Date.now(); + this.persist(); + } + return "existing"; + } + if (this.state.entries.some((entry) => entry.envelope.messageId === envelope.messageId)) { + throw new Error(`Boss message ID ${envelope.messageId} is queued with a different idempotency scope`); + } + if (this.state.entries.length >= MAX_BOSS_CONTROL_OUTBOX_ENTRIES) throw new Error("Durable Boss control outbox is full"); + this.state.entries.push({ + to, + envelope, + scope: candidateScope, + fingerprint: candidateFingerprint, + queuedAt: Date.now(), + state: "queued", + }); + this.persist(); + return "added"; + } + + markAccepted(idempotencyKey: string, messageId: string, deliveryId: string): "accepted" | "already-accepted" { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (!entry || entry.envelope.messageId !== messageId || !deliveryId) { + throw new Error("Boss acknowledgement does not match the durable outbox binding"); + } + if (entry.state === "accepted") { + if (entry.deliveryId !== deliveryId) throw new Error("Boss acknowledgement changed the durable deliveryId"); + return "already-accepted"; + } + entry.state = "accepted"; + entry.deliveryId = deliveryId; + this.persist(); + return "accepted"; + } + + removeCorrelated(idempotencyKey: string, messageId: string, deliveryId?: string): void { + const index = this.state.entries.findIndex((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (index < 0) throw new Error("Boss terminal result has no durable outbox binding"); + const entry = this.state.entries[index]; + if (entry.envelope.messageId !== messageId) throw new Error("Boss terminal result messageId does not match the durable caller"); + if (deliveryId === undefined) { + if (entry.state !== "queued") throw new Error("Boss post-acceptance failure omitted the durable deliveryId"); + } else if (entry.state !== "accepted" || entry.deliveryId !== deliveryId) { + throw new Error("Boss terminal result arrived before the matching durable acknowledgement"); + } + this.state.entries.splice(index, 1); + this.persist(); + } + + private load(): BossControlOutboxState { + if (!existsSync(this.path)) return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: [] }; + try { + const parsed: unknown = JSON.parse(readFileSync(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlOutbox"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed as Record; + if (!exactKeys(state, ["version", "entries"]) || state.version !== BOSS_CONTROL_OUTBOX_VERSION || !Array.isArray(state.entries)) { + throw new Error("invalid Boss outbox state"); + } + return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: state.entries.map(parseEntry) }; + } catch (error) { + const corruptPath = `${this.path}.corrupt-${Date.now()}`; + renameSync(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control outbox was corrupt and quarantined at ${corruptPath}`, { cause: error }); + } + } + + private persist(): void { + writeDurableJson(this.path, this.state); + } +} diff --git a/broker/authorization.test.ts b/broker/authorization.test.ts index 880dd5b..de42318 100644 --- a/broker/authorization.test.ts +++ b/broker/authorization.test.ts @@ -2,6 +2,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import { authorizeSessionAction, visibleSessions } from "./authorization.ts"; import type { SessionInfo } from "../types.ts"; +import { BOSS_PARTICIPANT_BINDING_VERSION, BOSS_RUN_FEATURE_CONTRACT, type BossParticipantRole } from "@dataforxyz/agent-intercom-core/boss"; function local(id: string): SessionInfo { return { id, name: id, cwd: "/tmp", model: "test", pid: 1, startedAt: 1, lastActivity: 1, origin: "local" }; @@ -32,6 +33,37 @@ const sessions = [ remote("child-b", "manager"), ]; +function boss( + id: string, + bossRunId: string, + participantId: string, + role: BossParticipantRole, + options: { manager?: string; assigned?: string[] } = {}, +): SessionInfo { + return { + ...local(id), + boss: { + featureContract: BOSS_RUN_FEATURE_CONTRACT, + binding: { + version: BOSS_PARTICIPANT_BINDING_VERSION, + bossRunId, + participantId, + role, + communicationProfile: role, + bindingEpoch: 1, + sessionId: id, + brokerGeneration: 1, + brokerBootInstance: "boot-1", + state: "active", + ...(options.manager === undefined ? {} : { assignedManagerParticipantId: options.manager }), + authorityTransitionId: "transition-1", + }, + brokerIdentityVerified: true, + ...(options.assigned === undefined ? {} : { assignedParticipantIds: options.assigned }), + }, + }; +} + test("phase one discovery and communication use the same ancestor-chain policy", () => { assert.equal(authorizeSessionAction(sessions, "root", "send", "manager").allowed, true); assert.equal(authorizeSessionAction(sessions, "manager", "ask", "root").allowed, true); @@ -47,3 +79,37 @@ test("visibility hides unauthorized sessions rather than revealing denial detail assert.deepEqual(visibleSessions(sessions, "root").map((session) => session.id).sort(), ["child-a", "child-b", "manager", "root", "unrelated"]); assert.deepEqual(visibleSessions(sessions, "unrelated").map((session) => session.id).sort(), ["root", "unrelated"]); }); + +test("Boss registrations are isolated from ordinary sessions and other runs", () => { + const bossSessions = [ + local("ordinary"), + boss("manager", "run-1", "manager-1", "manager", { assigned: ["worker-1"] }), + boss("worker", "run-1", "worker-1", "worker", { manager: "manager-1" }), + boss("other-run", "run-2", "worker-2", "worker", { manager: "manager-2" }), + ]; + assert.equal(authorizeSessionAction(bossSessions, "manager", "send", "worker").allowed, true); + assert.equal(authorizeSessionAction(bossSessions, "worker", "discover", "other-run").allowed, false); + assert.equal(authorizeSessionAction(bossSessions, "ordinary", "discover", "worker").allowed, false); + assert.deepEqual(visibleSessions(bossSessions, "worker").map((session) => session.id).sort(), ["manager", "worker"]); +}); + +test("Boss structured control uses the directional Core policy matrix", () => { + const bossSessions = [ + boss("manager", "run-1", "manager-1", "manager", { assigned: ["worker-1"] }), + boss("worker", "run-1", "worker-1", "worker", { manager: "manager-1" }), + ]; + assert.equal(authorizeSessionAction( + bossSessions, + "manager", + "control", + "worker", + { controlKind: "assignment_request", correlated: true }, + ).allowed, true); + assert.equal(authorizeSessionAction( + bossSessions, + "worker", + "control", + "manager", + { controlKind: "assignment_request", correlated: true }, + ).allowed, false); +}); diff --git a/broker/authorization.ts b/broker/authorization.ts index 225b986..05cb5fd 100644 --- a/broker/authorization.ts +++ b/broker/authorization.ts @@ -1,5 +1,11 @@ -import { authorize, type AuthorizationDecision, type PolicyAction, type PolicyPrincipal, type PolicyState } from "@dataforxyz/agent-intercom-core"; +import { type AuthorizationDecision, type PolicyAction, type PolicyPrincipal, type PolicyState } from "@dataforxyz/agent-intercom-core"; +import type { + BossAuthorizationContext, + BossPolicyAction, + FeatureAwareAuthorizationDecision, +} from "@dataforxyz/agent-intercom-core/boss"; import type { SessionInfo } from "../types.ts"; +import { authorizeBossAwareSessionAction } from "./boss-adapter.ts"; export function policyPrincipalForSession(session: SessionInfo): PolicyPrincipal { if (session.origin === "remote") { @@ -35,16 +41,11 @@ export function policyStateForSessions(sessions: Iterable): PolicyS export function authorizeSessionAction( sessions: Iterable, actorId: string, - action: PolicyAction, + action: PolicyAction | BossPolicyAction, targetId: string, -): AuthorizationDecision { - const state = policyStateForSessions(sessions); - const actor = state.principals[actorId]; - const target = state.principals[targetId]; - return authorize(state, actorId, action, targetId, { - actorGeneration: actor?.generation, - targetGeneration: target?.generation, - }); + bossContext?: BossAuthorizationContext, +): AuthorizationDecision | FeatureAwareAuthorizationDecision { + return authorizeBossAwareSessionAction(sessions, actorId, action, targetId, bossContext); } export function visibleSessions(sessions: Iterable, actorId: string): SessionInfo[] { diff --git a/broker/boss-adapter.test.ts b/broker/boss-adapter.test.ts new file mode 100644 index 0000000..9b6b7a7 --- /dev/null +++ b/broker/boss-adapter.test.ts @@ -0,0 +1,260 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + BOSS_PARTICIPANT_BINDING_VERSION, + BOSS_PARTICIPANT_CREDENTIAL_VERSION, + BOSS_RUN_FEATURE_CONTRACT, + INTERCOM_BASE_PROTOCOL_VERSION, +} from "@dataforxyz/agent-intercom-core/boss"; +import { + bossCapabilityAdvertisement, + hasAuthoritativeBossControlCorrelation, + exactRegistrationKind, + exactBossSessionTarget, + missingBossAdvertisementPredicates, + parseExactRegisteredFrame, + parseExactRegistrationFrame, + assertBossCanonicalData, + parseBossParticipantBindingMetadata, + parseBossParticipantRegistrationMetadata, +} from "./boss-adapter.ts"; + +test("Boss advertisement remains dormant until every lockstep predicate is ready", () => { + assert.equal(bossCapabilityAdvertisement(), undefined); + assert.deepEqual(missingBossAdvertisementPredicates(), [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth", + ]); + const advertisement = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true, + }); + assert.equal(advertisement?.baseProtocolVersion, INTERCOM_BASE_PROTOCOL_VERSION); + assert.equal(advertisement?.features[0]?.feature, "boss-run-v1"); +}); + +test("Boss readiness is an exact proxy-first descriptor schema", () => { + assert.throws(() => bossCapabilityAdvertisement({} as never)); + assert.throws(() => bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true, + extra: true, + } as never)); + for (const mutate of [ + (value: Record) => Object.defineProperty(value, "protectedProvider", { value: true, enumerable: false }), + (value: Record) => Object.defineProperty(value, "protectedProvider", { get: () => true, enumerable: true }), + (value: Record) => Object.defineProperty(value, Symbol("hidden"), { value: true, enumerable: true }), + (value: Record) => Object.setPrototypeOf(value, { inherited: true }), + ]) { + const value: Record = { + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true, + }; + mutate(value); + assert.throws(() => bossCapabilityAdvertisement(value as never)); + } + let trapCount = 0; + const proxy = new Proxy({}, { + get() { trapCount += 1; throw new Error("trap"); }, + ownKeys() { trapCount += 1; throw new Error("trap"); }, + getOwnPropertyDescriptor() { trapCount += 1; throw new Error("trap"); }, + getPrototypeOf() { trapCount += 1; throw new Error("trap"); }, + }); + assert.throws(() => bossCapabilityAdvertisement(proxy as never), /proxies are not supported/); + assert.equal(trapCount, 0); +}); + +test("canonical arrays reject sparse, inherited/custom, accessor, symbol, extra, duplicate, and coercible entries", () => { + const hostile: unknown[] = []; + hostile.push(new Array(1)); + const customPrototype = ["worker-1"]; + Object.setPrototypeOf(customPrototype, Object.create(Array.prototype)); + hostile.push(customPrototype); + const accessor = ["worker-1"]; + Object.defineProperty(accessor, "0", { get: () => "worker-1", enumerable: true, configurable: true }); + hostile.push(accessor); + const symbol = ["worker-1"]; + Object.defineProperty(symbol, Symbol("hidden"), { value: true }); + hostile.push(symbol); + const extra = ["worker-1"] as unknown[] & { extra?: boolean }; + extra.extra = true; + hostile.push(extra); + for (const value of hostile) assert.throws(() => assertBossCanonicalData(value)); +}); + +test("participant registration negotiates the exact Core feature and credential binding", () => { + const registration = parseBossParticipantRegistrationMetadata({ + featureContract: BOSS_RUN_FEATURE_CONTRACT, + credential: { + version: BOSS_PARTICIPANT_CREDENTIAL_VERSION, + namespace: "boss-run-v1", + credentialKind: "enrollment", + credentialId: "credential-1", + credential: "secret-token", + bossRunId: "run-1", + participantId: "worker-1", + role: "worker", + communicationProfile: "worker", + bindingEpoch: 1, + issuedAt: "2026-07-28T00:00:00.000Z", + expiresAt: "2026-07-28T01:00:00.000Z", + nonce: "nonce-1", + }, + }); + assert.equal(registration.featureContract.baseProtocolVersion, 3); + assert.equal(registration.credential.participantId, "worker-1"); +}); + +test("binding metadata is broker-owned and session-bound", () => { + const metadata = { + featureContract: BOSS_RUN_FEATURE_CONTRACT, + binding: { + version: BOSS_PARTICIPANT_BINDING_VERSION, + bossRunId: "run-1", + participantId: "manager-1", + role: "manager", + communicationProfile: "manager", + bindingEpoch: 1, + sessionId: "session-manager", + brokerGeneration: 1, + brokerBootInstance: "boot-1", + state: "active", + authorityTransitionId: "transition-1", + }, + brokerIdentityVerified: true, + assignedParticipantIds: ["worker-1"], + }; + assert.equal(parseBossParticipantBindingMetadata(metadata, "session-manager").binding.role, "manager"); + assert.throws(() => parseBossParticipantBindingMetadata(metadata, "session-substitution"), /registered intercom session/); +}); + +test("Boss metadata rejects proxies and sparse policy arrays", () => { + const metadata = { + featureContract: BOSS_RUN_FEATURE_CONTRACT, + binding: { + version: BOSS_PARTICIPANT_BINDING_VERSION, + bossRunId: "run-1", + participantId: "manager-1", + role: "manager", + communicationProfile: "manager", + bindingEpoch: 1, + sessionId: "session-manager", + brokerGeneration: 1, + brokerBootInstance: "boot-1", + state: "active", + authorityTransitionId: "transition-1", + }, + brokerIdentityVerified: true, + assignedParticipantIds: ["worker-1"], + }; + assert.throws( + () => parseBossParticipantBindingMetadata(new Proxy(metadata, {}), "session-manager"), + /proxies are not supported/, + ); + const sparse = { ...metadata, assignedParticipantIds: new Array(1) }; + assert.throws(() => parseBossParticipantBindingMetadata(sparse, "session-manager"), /dense array|sparse array holes/); + assert.throws(() => parseBossParticipantBindingMetadata({ ...metadata, assignedParticipantIds: ["worker-1", "worker-1"] }, "session-manager"), /unique participant list/); + assert.throws(() => parseBossParticipantBindingMetadata({ ...metadata, assignedParticipantIds: [1] }, "session-manager"), /unique participant list/); +}); + +test("dormant legacy broker never manufactures Boss correlation evidence", () => { + assert.equal(hasAuthoritativeBossControlCorrelation(), false); +}); + +test("Boss registration cannot be folded into an ordinary registration", () => { + assert.equal(exactRegistrationKind({}, undefined), "ordinary"); + assert.throws(() => exactRegistrationKind({}, "ordinary"), /must be absent/); + assert.throws(() => exactRegistrationKind({ boss: {} }, "ordinary"), /must be boss/); + assert.throws(() => exactRegistrationKind({}, "boss"), /must be absent/); +}); + +test("ordinary and Boss registration frames use exact non-folding discriminants", () => { + const ordinary = { + type: "register", + protocol: "pi-intercom", + version: 3, + session: { cwd: "/tmp", model: "gpt", pid: 1, startedAt: 1, lastActivity: 1 }, + }; + assert.equal(parseExactRegistrationFrame(ordinary).type, "register"); + for (const folded of [ + { registrationKind: "ordinary" }, + { capabilities: {} }, + { boss: {} }, + { featureContract: {} }, + { binding: {} }, + ]) assert.throws(() => parseExactRegistrationFrame({ ...ordinary, ...folded })); + assert.throws(() => parseExactRegistrationFrame({ ...ordinary, session: { ...ordinary.session, boss: {} } })); + + const registered = { type: "registered", sessionId: "session-1", protocol: "pi-intercom", version: 3 }; + assert.equal(parseExactRegisteredFrame(registered, "ordinary-local").type, "registered"); + for (const unsolicited of [ + { registrationKind: "ordinary" }, + { capabilities: {} }, + { boss: {} }, + { access: {} }, + ]) assert.throws(() => parseExactRegisteredFrame({ ...registered, ...unsolicited }, "ordinary-local")); +}); + +test("requested Boss registered frame requires exact capability echo and broker-owned binding", () => { + const capabilities = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true, + })!; + const boss = { + featureContract: BOSS_RUN_FEATURE_CONTRACT, + binding: { + version: BOSS_PARTICIPANT_BINDING_VERSION, + bossRunId: "run-1", + participantId: "manager-1", + role: "manager", + communicationProfile: "manager", + bindingEpoch: 1, + sessionId: "session-manager", + brokerGeneration: 1, + brokerBootInstance: "boot-1", + state: "active", + authorityTransitionId: "transition-1", + }, + brokerIdentityVerified: true, + assignedParticipantIds: [], + }; + const frame = { + type: "registered", + registrationKind: "boss", + sessionId: "session-manager", + protocol: "pi-intercom", + version: 3, + capabilities, + boss, + }; + assert.equal(parseExactRegisteredFrame(frame, "boss").type, "registered"); + assert.throws(() => parseExactRegisteredFrame({ ...frame, capabilities: { ...capabilities, features: [] } }, "boss")); + assert.throws(() => parseExactRegisteredFrame({ ...frame, capabilities: { ...capabilities, baseProtocolVersion: 99 } }, "boss")); + const { capabilities: _capabilities, ...withoutCapabilities } = frame; + assert.throws(() => parseExactRegisteredFrame(withoutCapabilities, "boss")); + const { boss: _boss, ...withoutBinding } = frame; + assert.throws(() => parseExactRegisteredFrame(withoutBinding, "boss")); +}); + +test("Boss routing accepts only the exact session ID, never names or prefixes", () => { + const session = { info: { id: "session-exact" } }; + const sessions = new Map([["session-exact", session]]); + assert.equal(exactBossSessionTarget(sessions, "session-exact"), session); + assert.equal(exactBossSessionTarget(sessions, "session"), undefined); + assert.equal(exactBossSessionTarget(sessions, "friendly-name"), undefined); +}); diff --git a/broker/boss-adapter.ts b/broker/boss-adapter.ts new file mode 100644 index 0000000..49388a2 --- /dev/null +++ b/broker/boss-adapter.ts @@ -0,0 +1,531 @@ +import { + BOSS_CAPABILITY_FEATURE_DIGEST, + BOSS_CONTROL_ENVELOPE_VERSION, + BOSS_POLICY_PRINCIPAL_VERSION, + BOSS_POLICY_SEMANTICS_HASH, + BOSS_RUN_FEATURE, + BOSS_RUN_FEATURE_CONTRACT, + BOSS_RUN_FEATURE_SEMANTICS_HASH, + BOSS_RUN_FEATURE_VERSION, + BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + BROKER_FEATURE_ATTESTATION_VERSION, + INTERCOM_BASE_PROTOCOL_VERSION, + authorizeFeatureAware, + brokerFeatureSetHash, + parseBossControlEnvelope, + parseBossParticipantBinding, + parseBossParticipantCredentialEnvelope, + parseBossRunFeatureContract, + parseBrokerCapabilityAdvertisement, + parseParticipantState, + parseWorkerIdentityV2, + type BossAuthorizationContext, + type BossControlEnvelope, + type BossControlKind, + type BossControlType, + type BossPolicyAction, + type BossPolicyPrincipal, + type BrokerCapabilityAdvertisement, + type FeatureAwareAuthorizationDecision, + type FeatureAwarePolicyState, +} from "@dataforxyz/agent-intercom-core/boss"; +import { + ContractValidationError, + assertExactKeys, + assertRecord, + canonicalJson, +} from "@dataforxyz/agent-intercom-core/canonical"; +import type { PolicyAction } from "@dataforxyz/agent-intercom-core/policy"; +import { types as nodeUtilTypes } from "node:util"; +import type { + BrokerMessage, + BossParticipantBindingMetadata, + BossParticipantRegistrationMetadata, + ClientMessage, + SessionInfo, +} from "../types.ts"; + +export const BOSS_ADVERTISEMENT_PREDICATES = [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth", +] as const; + +export type BossAdvertisementPredicate = (typeof BOSS_ADVERTISEMENT_PREDICATES)[number]; +export type BossAdvertisementReadiness = Readonly>; + +export const DORMANT_BOSS_ADVERTISEMENT_READINESS: BossAdvertisementReadiness = Object.freeze({ + protectedProvider: false, + brokerIdentity: false, + credentialRegistry: false, + authorityTransitions: false, + participantHealth: false, +}); + +const ORDINARY_SESSION_REGISTRATION_KEYS = [ + "cwd", + "model", + "pid", + "startedAt", + "lastActivity", +] as const; +const OPTIONAL_SESSION_REGISTRATION_KEYS = ["name", "status", "runtimeInstanceId"] as const; + +/** + * Core's canonical parsers reject exotic descriptors, but a Proxy can mimic a + * plain record and Array iteration skips holes. Reject both before any Boss + * value crosses the adapter trust boundary. + */ +export function assertBossCanonicalData(value: unknown, path = "$", seen = new WeakSet()): void { + if (typeof value !== "object" || value === null) return; + if (nodeUtilTypes.isProxy(value)) { + throw new ContractValidationError(path, "proxies are not supported"); + } + if (seen.has(value)) throw new ContractValidationError(path, "cyclic values are not supported"); + seen.add(value); + if (Array.isArray(value)) { + if (Object.getPrototypeOf(value) !== Array.prototype) { + throw new ContractValidationError(path, "must use the exact Array prototype"); + } + const ownKeys = Reflect.ownKeys(value); + const expectedKeys = new Set(["length"]); + for (let index = 0; index < value.length; index += 1) expectedKeys.add(String(index)); + if (ownKeys.length !== expectedKeys.size || ownKeys.some((key) => !expectedKeys.has(key))) { + throw new ContractValidationError(path, "must be a dense array without symbols or extra properties"); + } + for (let index = 0; index < value.length; index += 1) { + if (!Object.hasOwn(value, index)) { + throw new ContractValidationError(`${path}[${index}]`, "sparse array holes are not supported"); + } + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === undefined || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}[${index}]`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}[${index}]`, seen); + } + return; + } + if (Object.getPrototypeOf(value) !== Object.prototype) { + throw new ContractValidationError(path, "must use the exact Object prototype"); + } + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") throw new ContractValidationError(path, "symbol properties are not supported"); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === undefined || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}.${key}`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}.${key}`, seen); + } +} + +function parseBossAdvertisementReadiness(value: unknown): BossAdvertisementReadiness { + assertBossCanonicalData(value, "$.readiness"); + assertRecord(value); + assertExactKeys(value, BOSS_ADVERTISEMENT_PREDICATES); + const parsed = {} as Record; + for (const predicate of BOSS_ADVERTISEMENT_PREDICATES) { + const enabled = ownDataValue(value, predicate); + if (typeof enabled !== "boolean") { + throw new ContractValidationError(`$.readiness.${predicate}`, "must be a boolean"); + } + parsed[predicate] = enabled; + } + return parsed; +} + +export function missingBossAdvertisementPredicates( + readiness: BossAdvertisementReadiness = DORMANT_BOSS_ADVERTISEMENT_READINESS, +): BossAdvertisementPredicate[] { + const parsed = parseBossAdvertisementReadiness(readiness); + return BOSS_ADVERTISEMENT_PREDICATES.filter((predicate) => parsed[predicate] !== true); +} + +/** + * The Boss feature is intentionally absent until every lockstep service + * predicate exists. Merely importing Core contracts can never advertise it. + */ +export function bossCapabilityAdvertisement( + readiness: BossAdvertisementReadiness = DORMANT_BOSS_ADVERTISEMENT_READINESS, +): BrokerCapabilityAdvertisement | undefined { + if (missingBossAdvertisementPredicates(readiness).length > 0) return undefined; + const features = [{ + version: BROKER_FEATURE_ATTESTATION_VERSION, + feature: BOSS_RUN_FEATURE, + featureVersion: BOSS_RUN_FEATURE_VERSION, + semanticsHash: BOSS_RUN_FEATURE_SEMANTICS_HASH, + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST, + }]; + return parseBrokerCapabilityAdvertisement({ + baseProtocolVersion: INTERCOM_BASE_PROTOCOL_VERSION, + features, + protocolFeatureContractHash: BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + featureSetHash: brokerFeatureSetHash(features), + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST, + }); +} + +function optionalOwnDataValue(value: unknown, key: string): unknown { + assertRecord(value); + const descriptor = Object.getOwnPropertyDescriptor(value as object, key); + if (descriptor === undefined) return undefined; + if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`$.${key}`, "must be an own enumerable data property"); + } + return descriptor.value; +} + +function ownDataValue(value: unknown, key: string): unknown { + const result = optionalOwnDataValue(value, key); + if (result === undefined) throw new ContractValidationError(`$.${key}`, "is required"); + return result; +} + +export function parseBossParticipantRegistrationMetadata( + value: unknown, +): BossParticipantRegistrationMetadata { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys(value, ["featureContract", "credential"]); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if ( + featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION + || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT) + ) { + throw new ContractValidationError("$.featureContract", "must exactly negotiate boss-run-v1 over base protocol v3"); + } + const credential = parseBossParticipantCredentialEnvelope(ownDataValue(value, "credential")); + if (credential.namespace !== featureContract.feature) { + throw new ContractValidationError("$.credential.namespace", "must match the negotiated feature namespace"); + } + return { featureContract, credential }; +} + +export function exactRegistrationKind( + session: { boss?: unknown }, + value: unknown, +): "ordinary" | "boss" { + assertBossCanonicalData(session, "$.session"); + assertRecord(session); + const boss = optionalOwnDataValue(session, "boss"); + if (boss === undefined) { + if (value === undefined) return "ordinary"; + throw new ContractValidationError("$.registrationKind", "must be absent when Boss metadata is absent"); + } + if (value !== "boss") { + throw new ContractValidationError("$.registrationKind", "must be boss when Boss metadata is present"); + } + return "boss"; +} + +/** + * Validate the complete client registration discriminant before the broker + * projects any fields. Ordinary frames deliberately retain the pre-Boss wire + * shape: they have no registrationKind and no Boss capability/authority keys. + */ +export function parseExactRegistrationFrame(value: unknown): Extract { + assertBossCanonicalData(value, "$.register"); + assertRecord(value); + const session = ownDataValue(value, "session"); + assertRecord(session); + const registrationKind = optionalOwnDataValue(value, "registrationKind"); + const kind = exactRegistrationKind(session, registrationKind); + if (kind === "ordinary") { + assertExactKeys(value, ["type", "protocol", "version", "session"], ["sessionId", "stateId", "access"]); + assertExactKeys(session, ORDINARY_SESSION_REGISTRATION_KEYS, OPTIONAL_SESSION_REGISTRATION_KEYS); + } else { + assertExactKeys(value, ["type", "registrationKind", "protocol", "version", "session"], ["sessionId", "stateId"]); + assertExactKeys(session, [...ORDINARY_SESSION_REGISTRATION_KEYS, "boss"], OPTIONAL_SESSION_REGISTRATION_KEYS); + parseBossParticipantRegistrationMetadata(ownDataValue(session, "boss")); + } + if (ownDataValue(value, "type") !== "register") { + throw new ContractValidationError("$.register.type", "must be register"); + } + return value as Extract; +} + +/** Validate the exact broker response shape for the registration requested. */ +export function parseExactRegisteredFrame( + value: unknown, + expected: "ordinary-local" | "ordinary-remote" | "boss", +): Extract { + assertBossCanonicalData(value, "$.registered"); + assertRecord(value); + if (expected === "boss") { + assertExactKeys(value, ["type", "registrationKind", "sessionId", "protocol", "version", "capabilities", "boss"]); + if (ownDataValue(value, "registrationKind") !== "boss") { + throw new ContractValidationError("$.registered.registrationKind", "must be boss"); + } + const sessionId = ownDataValue(value, "sessionId"); + if (typeof sessionId !== "string" || sessionId.length === 0) { + throw new ContractValidationError("$.registered.sessionId", "must be a non-empty string"); + } + const advertisement = parseBrokerCapabilityAdvertisement(ownDataValue(value, "capabilities")); + const expectedAdvertisement = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true, + })!; + const bossFeature = advertisement.features.find((feature) => feature.feature === BOSS_RUN_FEATURE); + if ( + bossFeature === undefined + || canonicalJson(bossFeature) !== canonicalJson(expectedAdvertisement.features[0]) + || advertisement.baseProtocolVersion !== expectedAdvertisement.baseProtocolVersion + || advertisement.protocolFeatureContractHash !== expectedAdvertisement.protocolFeatureContractHash + || advertisement.controlEnvelopeVersion !== expectedAdvertisement.controlEnvelopeVersion + || advertisement.capabilityDigest !== expectedAdvertisement.capabilityDigest + ) throw new ContractValidationError("$.registered.capabilities", "must exactly echo the requested boss-run-v1 contract"); + parseBossParticipantBindingMetadata(ownDataValue(value, "boss"), sessionId); + } else if (expected === "ordinary-remote") { + assertExactKeys(value, ["type", "sessionId", "protocol", "version", "remoteAccess", "access"]); + } else { + assertExactKeys(value, ["type", "sessionId", "protocol", "version"]); + } + if (ownDataValue(value, "type") !== "registered") { + throw new ContractValidationError("$.registered.type", "must be registered"); + } + return value as Extract; +} + +export function parseBossParticipantBindingMetadata( + value: unknown, + expectedSessionId?: string, +): BossParticipantBindingMetadata { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys( + value, + ["featureContract", "binding", "brokerIdentityVerified"], + ["assignedParticipantIds", "requestingPrincipalId", "workerIdentity", "participantState"], + ); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if ( + featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION + || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT) + ) { + throw new ContractValidationError("$.featureContract", "must exactly bind boss-run-v1 over base protocol v3"); + } + const binding = parseBossParticipantBinding(ownDataValue(value, "binding")); + if (ownDataValue(value, "brokerIdentityVerified") !== true) { + throw new ContractValidationError("$.brokerIdentityVerified", "must be true for a broker-owned Boss binding"); + } + if (expectedSessionId !== undefined && binding.sessionId !== expectedSessionId) { + throw new ContractValidationError("$.binding.sessionId", "must match the registered intercom session"); + } + const rawAssignedParticipantIds = optionalOwnDataValue(value, "assignedParticipantIds"); + let assignedParticipantIds: string[] | undefined; + if (rawAssignedParticipantIds !== undefined) { + if ( + binding.role !== "manager" + || !Array.isArray(rawAssignedParticipantIds) + || rawAssignedParticipantIds.some((entry) => typeof entry !== "string" || entry.length === 0) + || new Set(rawAssignedParticipantIds).size !== rawAssignedParticipantIds.length + ) { + throw new ContractValidationError("$.assignedParticipantIds", "must be a unique participant list present only for a Manager"); + } + assignedParticipantIds = rawAssignedParticipantIds as string[]; + } + if (binding.role === "manager" && assignedParticipantIds === undefined) { + throw new ContractValidationError("$.assignedParticipantIds", "is required for a Manager policy binding"); + } + const rawRequestingPrincipalId = optionalOwnDataValue(value, "requestingPrincipalId"); + if ((binding.role === "council") !== (typeof rawRequestingPrincipalId === "string" && rawRequestingPrincipalId.length > 0)) { + throw new ContractValidationError("$.requestingPrincipalId", "is required exactly for a Council policy binding"); + } + const requestingPrincipalId = typeof rawRequestingPrincipalId === "string" ? rawRequestingPrincipalId : undefined; + const rawWorkerIdentity = optionalOwnDataValue(value, "workerIdentity"); + const rawParticipantState = optionalOwnDataValue(value, "participantState"); + if ((rawWorkerIdentity === undefined) !== (rawParticipantState === undefined)) { + throw new ContractValidationError("$.workerIdentity", "workerIdentity and participantState must be supplied together"); + } + const workerIdentity = rawWorkerIdentity === undefined ? undefined : parseWorkerIdentityV2(rawWorkerIdentity); + const participantState = rawParticipantState === undefined + ? undefined + : parseParticipantState(rawParticipantState, "$.participantState"); + if ( + workerIdentity !== undefined + && ( + !("bossRunId" in workerIdentity) + || workerIdentity.bossRunId !== binding.bossRunId + || workerIdentity.participantId !== binding.participantId + || workerIdentity.bindingEpoch !== binding.bindingEpoch + ) + ) throw new ContractValidationError("$.workerIdentity", "must match the broker-owned participant binding"); + return { + featureContract, + binding, + brokerIdentityVerified: true, + ...(assignedParticipantIds === undefined ? {} : { assignedParticipantIds: [...assignedParticipantIds] }), + ...(requestingPrincipalId === undefined ? {} : { requestingPrincipalId }), + ...(workerIdentity === undefined ? {} : { workerIdentity, participantState: participantState! }), + }; +} + +function bossPrincipal(session: SessionInfo, metadata: BossParticipantBindingMetadata): BossPolicyPrincipal { + const { binding } = metadata; + return { + version: BOSS_POLICY_PRINCIPAL_VERSION, + principalId: session.id, + principalClass: "boss-private", + state: binding.state, + bossRunId: binding.bossRunId, + participantId: binding.participantId, + role: binding.role, + bindingEpoch: binding.bindingEpoch, + ...(binding.assignedManagerParticipantId === undefined + ? {} + : { assignedManagerParticipantId: binding.assignedManagerParticipantId }), + ...(metadata.assignedParticipantIds === undefined + ? {} + : { assignedParticipantIds: metadata.assignedParticipantIds }), + ...(metadata.requestingPrincipalId === undefined + ? {} + : { requestingPrincipalId: metadata.requestingPrincipalId }), + }; +} + +export function featurePolicyStateForSessions(sessions: Iterable): FeatureAwarePolicyState { + const legacy: FeatureAwarePolicyState["legacy"] = { principals: {} }; + const boss: FeatureAwarePolicyState["boss"] = { principals: {} }; + const registrations: FeatureAwarePolicyState["registrations"] = {}; + for (const session of sessions) { + if (session.boss !== undefined) { + const metadata = parseBossParticipantBindingMetadata(session.boss, session.id); + boss.principals[session.id] = bossPrincipal(session, metadata); + registrations[session.id] = { + principalId: session.id, + principalClass: "boss-bound", + state: metadata.binding.state, + bossRunId: metadata.binding.bossRunId, + participantId: metadata.binding.participantId, + bindingEpoch: metadata.binding.bindingEpoch, + featureContract: metadata.featureContract, + policySemanticsHash: BOSS_POLICY_SEMANTICS_HASH, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST, + brokerIdentityVerified: metadata.brokerIdentityVerified, + }; + continue; + } + const principal = session.origin === "remote" + ? (() => { + if (!session.parentSessionId || !session.rootSessionId || !session.generation) { + throw new Error(`Remote session ${session.id} is missing broker-owned policy metadata`); + } + return { + id: session.id, + kind: "remote" as const, + state: "active" as const, + generation: session.generation, + policy: "remote-tree" as const, + parentSessionId: session.parentSessionId, + rootSessionId: session.rootSessionId, + }; + })() + : { + id: session.id, + kind: "local" as const, + state: "active" as const, + generation: 1, + policy: "local-public" as const, + rootSessionId: session.id, + }; + legacy.principals[session.id] = principal; + registrations[session.id] = { principalId: session.id, principalClass: "ordinary", state: "active" }; + } + return { legacy, boss, registrations }; +} + +export function authorizeBossAwareSessionAction( + sessions: Iterable, + actorId: string, + action: PolicyAction | BossPolicyAction, + targetId: string, + bossContext?: BossAuthorizationContext, +): FeatureAwareAuthorizationDecision { + const values = Array.from(sessions); + const state = featurePolicyStateForSessions(values); + const actor = state.registrations[actorId]; + const target = state.registrations[targetId]; + return authorizeFeatureAware(state, { + actorId, + action, + targetId, + ...(actor?.principalClass === "ordinary" && target?.principalClass === "ordinary" + ? { + legacyContext: { + actorGeneration: state.legacy.principals[actorId]?.generation, + targetGeneration: state.legacy.principals[targetId]?.generation, + }, + } + : bossContext === undefined ? {} : { bossContext }), + }); +} + +const BOSS_CONTROL_KIND_BY_TYPE: Readonly> = { + "boss.assignment.created": "assignment_request", + "boss.assignment.accepted": "assignment_response", + "boss.assignment.checkpoint": "assignment_response", + "boss.assignment.submitted": "assignment_response", + "boss.assignment.rejected": "assignment_response", + "boss.assignment.cancelled": "lifecycle", + "boss.staffing.requested": "staffing", + "boss.staffing.resolved": "staffing", + "boss.review.requested": "review_request", + "boss.review.submitted": "review_result", + "boss.council.requested": "review_request", + "boss.council.submitted": "review_result", + "boss.proof.submitted": "proof", + "boss.worker.health": "health", + "boss.worker.blocked": "health", + "boss.worker.failed": "health", + "boss.worker.notice": "lifecycle", + "boss.worker.notice_delivery_failed": "lifecycle", + "boss.decision.required": "decision", +}; + +export function bossControlKind(envelopeValue: unknown): { + envelope: BossControlEnvelope; + controlKind: BossControlKind; +} { + assertBossCanonicalData(envelopeValue); + const envelope = parseBossControlEnvelope(envelopeValue); + return { envelope, controlKind: BOSS_CONTROL_KIND_BY_TYPE[envelope.type] }; +} + +/** The legacy broker has no authoritative Boss causation ledger. */ +export function hasAuthoritativeBossControlCorrelation(): false { + return false; +} + +export function exactBossSessionTarget( + sessions: ReadonlyMap, + requestedSessionId: string, +): T | undefined { + const target = sessions.get(requestedSessionId); + return target?.info.id === requestedSessionId ? target : undefined; +} + +export function assertBossControlSender( + session: SessionInfo, + envelopeValue: unknown, +): BossControlEnvelope { + const { envelope } = bossControlKind(envelopeValue); + if (session.boss === undefined) { + throw new ContractValidationError("$.session", "ordinary sessions cannot originate Boss control envelopes"); + } + const { binding } = parseBossParticipantBindingMetadata(session.boss, session.id); + if ( + binding.state !== "active" + || envelope.bossRunId !== binding.bossRunId + || envelope.participantId !== binding.participantId + || envelope.bindingEpoch !== binding.bindingEpoch + ) { + throw new ContractValidationError("$.envelope", "does not match the active broker-owned participant binding"); + } + return envelope; +} diff --git a/broker/boss-control-ledger.test.ts b/broker/boss-control-ledger.test.ts new file mode 100644 index 0000000..e1d8901 --- /dev/null +++ b/broker/boss-control-ledger.test.ts @@ -0,0 +1,233 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; +import { + BossControlResultLedger, + bossControlAcceptedRecoveryFrames, + bossControlReplayFrames, + parseBossControlAck, + parseBossControlResult, + rebindBossControlResult, +} from "./boss-control-ledger.ts"; + +const scope = "a".repeat(64); +const fingerprint = "b".repeat(64); +const delivered = { + type: "boss_control_result" as const, + requestId: "request-1", + messageId: "request-1", + idempotencyKey: "operation-1", + status: "delivered" as const, + delivered: true as const, + deliveryId: "delivery-1", +}; + +test("Boss ledger durably persists accepted before terminal and rebinds replay to a new caller message", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-ledger-")); + try { + const path = join(dir, "ledger.json"); + const ledger = new BossControlResultLedger(path, () => 100); + assert.throws(() => ledger.recordTerminal(scope, fingerprint, delivered), /requires the matching durable accepted state/); + ledger.recordAccepted(scope, fingerprint, "delivery-1"); + assert.deepEqual(new BossControlResultLedger(path, () => 150).lookup(scope, fingerprint), { + status: "accepted", + deliveryId: "delivery-1", + }); + ledger.recordTerminal(scope, fingerprint, delivered); + const reloaded = new BossControlResultLedger(path, () => 200); + const replay = reloaded.lookup(scope, fingerprint); + assert.equal(replay.status, "replay"); + if (replay.status === "replay") { + assert.deepEqual(rebindBossControlResult(replay.result, "request-2"), { + ...delivered, + requestId: "request-2", + messageId: "request-2", + }); + const frames = bossControlReplayFrames(replay.result, "request-2"); + assert.deepEqual(frames.map((frame) => frame.type), ["boss_control_ack", "boss_control_result"]); + assert.equal(frames[0].deliveryId, "delivery-1"); + assert.equal(frames[1].messageId, "request-2"); + } + assert.deepEqual(reloaded.lookup(scope, "c".repeat(64)), { status: "conflict" }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("Boss result-order probes reject mismatched delivery and post-accept failures without deliveryId", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-order-")); + try { + const ledger = new BossControlResultLedger(join(dir, "ledger.json"), () => 100); + ledger.recordAccepted(scope, fingerprint, "delivery-1"); + assert.throws(() => ledger.recordTerminal(scope, fingerprint, { ...delivered, deliveryId: "delivery-2" }), /matching durable accepted state/); + assert.throws(() => ledger.recordTerminal(scope, fingerprint, { + type: "boss_control_result", + requestId: "request-1", + messageId: "request-1", + idempotencyKey: "operation-1", + status: "rejected", + delivered: false, + code: "DELIVERY_TIMEOUT", + reason: "timeout", + }), /must carry the accepted deliveryId/); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("Boss accepted and terminal bindings survive more than ten minutes offline and broker restarts", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-retention-")); + try { + const path = join(dir, "ledger.json"); + new BossControlResultLedger(path, () => 100).recordAccepted(scope, fingerprint, "delivery-1"); + + const afterAcceptedOffline = new BossControlResultLedger(path, () => 100 + 11 * 60 * 1000); + assert.deepEqual(afterAcceptedOffline.lookup(scope, fingerprint), { + status: "accepted", + deliveryId: "delivery-1", + }); + afterAcceptedOffline.recordTerminal(scope, fingerprint, delivered); + + const afterTerminalOffline = new BossControlResultLedger(path, () => 100 + 22 * 60 * 1000); + assert.equal(afterTerminalOffline.lookup(scope, fingerprint).status, "replay"); + assert.deepEqual(afterTerminalOffline.lookup(scope, "c".repeat(64)), { status: "conflict" }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("expired version-2 Boss records migrate without pruning", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-migrate-")); + try { + const path = join(dir, "ledger.json"); + const acceptedScope = "d".repeat(64); + writeFileSync(path, JSON.stringify({ + version: 2, + entries: [ + { + scope, + fingerprint, + expiresAt: 200, + state: "terminal", + result: delivered, + }, + { + scope: acceptedScope, + fingerprint, + expiresAt: 200, + state: "accepted", + deliveryId: "delivery-2", + }, + ], + })); + + const migrated = new BossControlResultLedger(path, () => 100 + 11 * 60 * 1000); + assert.equal(migrated.lookup(scope, fingerprint).status, "replay"); + assert.deepEqual(migrated.lookup(acceptedScope, fingerprint), { + status: "accepted", + deliveryId: "delivery-2", + }); + assert.deepEqual(JSON.parse(readFileSync(path, "utf8")), { + version: 3, + entries: [ + { scope, fingerprint, state: "terminal", result: delivered }, + { scope: acceptedScope, fingerprint, state: "accepted", deliveryId: "delivery-2" }, + ], + }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("accepted recovery denial emits the stable ACK before its terminal result", () => { + const result = { + type: "boss_control_result" as const, + requestId: "request-2", + messageId: "request-2", + idempotencyKey: "operation-1", + status: "rejected" as const, + delivered: false as const, + code: "SESSION_NOT_FOUND" as const, + reason: "target disappeared", + deliveryId: "delivery-1", + }; + const frames = bossControlAcceptedRecoveryFrames(result); + assert.deepEqual(frames.map((frame) => frame.type), ["boss_control_ack", "boss_control_result"]); + assert.equal(frames[0].deliveryId, "delivery-1"); + assert.equal(frames[1], result); + assert.throws(() => bossControlAcceptedRecoveryFrames({ ...result, deliveryId: undefined })); +}); + +test("Boss result and ACK schemas reject contradictory extras, unknown codes, descriptors, prototypes, symbols, and proxies", () => { + const ack = { + type: "boss_control_ack", + requestId: "request-1", + messageId: "request-1", + idempotencyKey: "operation-1", + status: "accepted", + deliveryId: "delivery-1", + }; + assert.deepEqual(parseBossControlAck(ack), ack); + assert.deepEqual(parseBossControlResult(delivered), delivered); + assert.throws(() => parseBossControlResult({ ...delivered, code: "POLICY_DENIED" }), /discriminant/); + assert.throws(() => parseBossControlResult({ ...delivered, extra: true }), /discriminant/); + assert.throws(() => parseBossControlResult({ + type: "boss_control_result", + requestId: "request-1", + messageId: "request-1", + idempotencyKey: "operation-1", + status: "rejected", + delivered: false, + code: "ATTACKER_CODE", + reason: "no", + }), /discriminant/); + assert.throws(() => parseBossControlResult({ + type: "boss_control_result", + requestId: "request-1", + messageId: "request-1", + idempotencyKey: "operation-1", + status: "rejected", + delivered: false, + code: "POLICY_DENIED", + reason: "no", + deliveryId: undefined, + }), /discriminant/); + assert.throws(() => parseBossControlAck({ ...ack, delivered: true }), /discriminant/); + + const hostile: unknown[] = []; + hostile.push(Object.assign(Object.create({ inherited: true }), ack)); + const symbol = { ...ack }; + Object.defineProperty(symbol, Symbol("hidden"), { value: true }); + hostile.push(symbol); + const nonEnumerable = { ...ack }; + Object.defineProperty(nonEnumerable, "status", { value: "accepted", enumerable: false }); + hostile.push(nonEnumerable); + const accessor = { ...ack }; + Object.defineProperty(accessor, "status", { get: () => "accepted", enumerable: true }); + hostile.push(accessor); + for (const value of hostile) assert.throws(() => parseBossControlAck(value)); + + let trapCount = 0; + const proxy = new Proxy(ack, { + get() { trapCount += 1; throw new Error("trap"); }, + ownKeys() { trapCount += 1; throw new Error("trap"); }, + getOwnPropertyDescriptor() { trapCount += 1; throw new Error("trap"); }, + getPrototypeOf() { trapCount += 1; throw new Error("trap"); }, + }); + assert.throws(() => parseBossControlAck(proxy), /proxies are not supported/); + assert.equal(trapCount, 0); +}); + +test("corrupt Boss ledger is quarantined and fails closed", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-control-corrupt-")); + try { + const path = join(dir, "ledger.json"); + writeFileSync(path, "{not-json"); + assert.throws(() => new BossControlResultLedger(path, () => 123), /corrupt and quarantined/); + assert.deepEqual(readdirSync(dir), ["ledger.json.corrupt-123"]); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/broker/boss-control-ledger.ts b/broker/boss-control-ledger.ts new file mode 100644 index 0000000..9902acc --- /dev/null +++ b/broker/boss-control-ledger.ts @@ -0,0 +1,306 @@ +import { existsSync, readFileSync, renameSync } from "node:fs"; +import { canonicalJson } from "@dataforxyz/agent-intercom-core/canonical"; +import { writeDurableJson } from "../durable-json.ts"; +import { assertBossCanonicalData } from "./boss-adapter.ts"; +import { restrictIntercomRuntimeFile } from "./paths.ts"; +import type { BrokerMessage } from "../types.ts"; + +const BOSS_CONTROL_LEDGER_VERSION = 3; +const EXPIRING_BOSS_CONTROL_LEDGER_VERSION = 2; +const MAX_BOSS_CONTROL_RESULTS = 2048; +const BOSS_CONTROL_FAILURE_CODES = new Set([ + "INVALID_CONTROL", + "IDEMPOTENCY_CONFLICT", + "SESSION_NOT_FOUND", + "POLICY_DENIED", + "RECIPIENT_DISCONNECTED", + "DELIVERY_TIMEOUT", +]); + +export type BossControlResult = Extract; +export type BossControlAck = Extract; + +interface BossControlLedgerEntryBase { + scope: string; + fingerprint: string; +} + +interface AcceptedBossControlLedgerEntry extends BossControlLedgerEntryBase { + state: "accepted"; + deliveryId: string; +} + +interface TerminalBossControlLedgerEntry extends BossControlLedgerEntryBase { + state: "terminal"; + result: BossControlResult; +} + +type BossControlLedgerEntry = AcceptedBossControlLedgerEntry | TerminalBossControlLedgerEntry; + +interface BossControlLedgerState { + version: typeof BOSS_CONTROL_LEDGER_VERSION; + entries: BossControlLedgerEntry[]; +} + +function exactStringKeys(value: Record, required: readonly string[], optional: readonly string[] = []): boolean { + const keys = Reflect.ownKeys(value); + const permitted = new Set([...required, ...optional]); + return required.every((key) => Object.hasOwn(value, key)) + && keys.every((key) => typeof key === "string" && permitted.has(key)); +} + +export function parseBossControlResult(value: unknown): BossControlResult { + assertBossCanonicalData(value, "$.bossControlResult"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control result must be an exact plain object"); + } + const result = value as Record; + const base = typeof result.requestId === "string" + && result.requestId.length > 0 + && result.messageId === result.requestId + && typeof result.idempotencyKey === "string" + && result.idempotencyKey.length > 0; + if (!base || result.type !== "boss_control_result") throw new Error("Invalid Boss control result binding"); + if ( + result.status === "delivered" + && result.delivered === true + && typeof result.deliveryId === "string" + && result.deliveryId.length > 0 + && exactStringKeys(result, ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "deliveryId"]) + ) return result as unknown as BossControlResult; + if ( + result.status === "rejected" + && result.delivered === false + && typeof result.code === "string" + && BOSS_CONTROL_FAILURE_CODES.has(result.code) + && typeof result.reason === "string" + && result.reason.length > 0 + && (!Object.hasOwn(result, "deliveryId") || (typeof result.deliveryId === "string" && result.deliveryId.length > 0)) + && exactStringKeys( + result, + ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "code", "reason"], + ["deliveryId"], + ) + ) return result as unknown as BossControlResult; + throw new Error("Invalid Boss control result discriminant"); +} + +export function parseBossControlAck(value: unknown): BossControlAck { + assertBossCanonicalData(value, "$.bossControlAck"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control acknowledgement must be an exact plain object"); + } + const ack = value as Record; + if ( + !exactStringKeys(ack, ["type", "requestId", "messageId", "idempotencyKey", "status", "deliveryId"]) + || ack.type !== "boss_control_ack" + || typeof ack.requestId !== "string" + || ack.requestId.length === 0 + || ack.messageId !== ack.requestId + || typeof ack.idempotencyKey !== "string" + || ack.idempotencyKey.length === 0 + || ack.status !== "accepted" + || typeof ack.deliveryId !== "string" + || ack.deliveryId.length === 0 + ) throw new Error("Invalid Boss control acknowledgement discriminant"); + return ack as unknown as BossControlAck; +} + +export function rebindBossControlResult(resultValue: unknown, messageId: string): BossControlResult { + if (typeof messageId !== "string" || messageId.length === 0) throw new Error("Replay messageId is required"); + const result = parseBossControlResult(resultValue); + return parseBossControlResult({ ...result, requestId: messageId, messageId }); +} + +export function bossControlReplayFrames(resultValue: unknown, messageId: string): [BossControlResult] | [BossControlAck, BossControlResult] { + const result = rebindBossControlResult(resultValue, messageId); + if (result.deliveryId === undefined) return [result]; + return [{ + type: "boss_control_ack", + requestId: messageId, + messageId, + idempotencyKey: result.idempotencyKey, + status: "accepted", + deliveryId: result.deliveryId, + }, result]; +} + +/** + * A caller recovering a durable accepted entry is newly attached to the + * stable delivery. It must observe that accepted transition before any + * terminal denial produced while the broker revalidates the target. + */ +export function bossControlAcceptedRecoveryFrames( + resultValue: unknown, +): [BossControlAck, BossControlResult] { + const result = parseBossControlResult(resultValue); + if (result.status !== "rejected" || result.deliveryId === undefined) { + throw new Error("Accepted Boss recovery requires a delivery-bound rejected result"); + } + return [{ + type: "boss_control_ack", + requestId: result.requestId, + messageId: result.messageId, + idempotencyKey: result.idempotencyKey, + status: "accepted", + deliveryId: result.deliveryId, + }, result]; +} + +function parseHash(value: unknown, field: string): string { + if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid Boss ledger ${field}`); + return value; +} + +function parseEntry(value: unknown, version: number): BossControlLedgerEntry { + assertBossCanonicalData(value, "$.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss ledger entry"); + const entry = value as Record; + const legacyExpiry = version === EXPIRING_BOSS_CONTROL_LEDGER_VERSION; + if (legacyExpiry && (typeof entry.expiresAt !== "number" || !Number.isSafeInteger(entry.expiresAt))) { + throw new Error("Invalid Boss ledger expiry"); + } + const base: BossControlLedgerEntryBase = { + scope: parseHash(entry.scope, "scope"), + fingerprint: parseHash(entry.fingerprint, "fingerprint"), + }; + const baseKeys = legacyExpiry + ? ["scope", "fingerprint", "expiresAt", "state"] + : ["scope", "fingerprint", "state"]; + if ( + entry.state === "accepted" + && exactStringKeys(entry, [...baseKeys, "deliveryId"]) + && typeof entry.deliveryId === "string" + && entry.deliveryId.length > 0 + ) return { ...base, state: "accepted", deliveryId: entry.deliveryId }; + if (entry.state === "terminal" && exactStringKeys(entry, [...baseKeys, "result"])) { + return { ...base, state: "terminal", result: parseBossControlResult(entry.result) }; + } + throw new Error("Invalid Boss ledger state discriminant"); +} + +export type BossControlLedgerLookup = + | { status: "miss" } + | { status: "conflict" } + | { status: "accepted"; deliveryId: string } + | { status: "replay"; result: BossControlResult }; + +/** + * Boss bindings outlive transport reconnects and the ordinary recent-delivery + * cache. Until the broker has an authoritative Boss run/binding teardown + * signal, retaining every accepted binding and terminal tombstone is the only + * fail-closed policy. Capacity exhaustion therefore rejects new work instead + * of silently forgetting an idempotency scope. + */ +export class BossControlResultLedger { + private state: BossControlLedgerState; + + constructor( + private readonly path: string, + private readonly now: () => number = Date.now, + ) { + const loaded = this.load(); + this.state = loaded.state; + if (loaded.migrated) this.persist(); + } + + lookup(scope: string, fingerprint: string): BossControlLedgerLookup { + const entry = this.state.entries.find((candidate) => candidate.scope === scope); + if (!entry) return { status: "miss" }; + if (entry.fingerprint !== fingerprint) return { status: "conflict" }; + return entry.state === "accepted" + ? { status: "accepted", deliveryId: entry.deliveryId } + : { status: "replay", result: structuredClone(entry.result) }; + } + + recordAccepted(scope: string, fingerprint: string, deliveryId: string): void { + if (!/^[a-f0-9]{64}$/.test(scope) || !/^[a-f0-9]{64}$/.test(fingerprint) || !deliveryId) { + throw new Error("Invalid Boss accepted-state binding"); + } + const existing = this.state.entries.find((entry) => entry.scope === scope); + if (existing) { + if (existing.fingerprint !== fingerprint || existing.state !== "accepted" || existing.deliveryId !== deliveryId) { + throw new Error("Boss idempotency scope is already bound to a different canonical state"); + } + return; + } + this.reserveCapacity(); + this.state.entries.push({ scope, fingerprint, state: "accepted", deliveryId }); + this.persist(); + } + + recordTerminal(scope: string, fingerprint: string, resultValue: unknown): void { + const result = parseBossControlResult(resultValue); + canonicalJson(result); + const existing = this.state.entries.find((entry) => entry.scope === scope); + if (existing?.fingerprint !== undefined && existing.fingerprint !== fingerprint) { + throw new Error("Boss idempotency scope is already bound to a different canonical request"); + } + if (existing?.state === "terminal") { + const existingStable = { ...existing.result, requestId: "", messageId: "" }; + const resultStable = { ...result, requestId: "", messageId: "" }; + if (canonicalJson(existingStable) !== canonicalJson(resultStable)) { + throw new Error("Boss idempotency scope is already bound to a different canonical result"); + } + return; + } + if (result.status === "delivered" || result.deliveryId !== undefined) { + if (existing?.state !== "accepted" || existing.deliveryId !== result.deliveryId) { + throw new Error("Boss terminal delivery requires the matching durable accepted state"); + } + } else if (existing?.state === "accepted") { + throw new Error("A terminal result after acceptance must carry the accepted deliveryId"); + } + const terminal: TerminalBossControlLedgerEntry = { + scope, + fingerprint, + state: "terminal", + result, + }; + if (existing) this.state.entries[this.state.entries.indexOf(existing)] = terminal; + else { + this.reserveCapacity(); + this.state.entries.push(terminal); + } + this.persist(); + } + + private reserveCapacity(): void { + if (this.state.entries.length >= MAX_BOSS_CONTROL_RESULTS) { + throw new Error("Durable Boss control ledger is full"); + } + } + + private load(): { state: BossControlLedgerState; migrated: boolean } { + if (!existsSync(this.path)) { + return { state: { version: BOSS_CONTROL_LEDGER_VERSION, entries: [] }, migrated: false }; + } + try { + const parsed: unknown = JSON.parse(readFileSync(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlLedger"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed as Record; + if ( + !exactStringKeys(state, ["version", "entries"]) + || (state.version !== BOSS_CONTROL_LEDGER_VERSION && state.version !== EXPIRING_BOSS_CONTROL_LEDGER_VERSION) + || !Array.isArray(state.entries) + ) throw new Error("invalid ledger state"); + return { + state: { + version: BOSS_CONTROL_LEDGER_VERSION, + entries: state.entries.map((entry) => parseEntry(entry, state.version as number)), + }, + migrated: state.version === EXPIRING_BOSS_CONTROL_LEDGER_VERSION, + }; + } catch (error) { + const corruptPath = `${this.path}.corrupt-${this.now()}`; + renameSync(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control ledger was corrupt and quarantined at ${corruptPath}`, { cause: error }); + } + } + + private persist(): void { + writeDurableJson(this.path, this.state); + } +} diff --git a/broker/broker.ts b/broker/broker.ts index b2b3c03..8a55d1a 100644 --- a/broker/broker.ts +++ b/broker/broker.ts @@ -2,7 +2,10 @@ import net from "net"; import { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from "fs"; import { join } from "path"; import { randomUUID } from "crypto"; +import { types as nodeUtilTypes } from "node:util"; import { authorize, POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION, type PolicyAction, type PolicyState } from "@dataforxyz/agent-intercom-core"; +import type { BossAuthorizationContext, BossControlEnvelope, BossPolicyAction } from "@dataforxyz/agent-intercom-core/boss"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; import { writeMessage, createMessageReader } from "./framing.ts"; import { ensureIntercomRuntimeDir, @@ -25,7 +28,22 @@ import { writeDurableJson } from "../durable-json.ts"; import { acquireBrokerOwnership, hasBrokerOwnership, releaseBrokerOwnership } from "./ownership.ts"; import { RemoteAccessRegistry, type RemotePrincipalMetadata, type RemotePrincipalRecord } from "./access-registry.ts"; import { authorizeSessionAction, visibleSessions } from "./authorization.ts"; +import { + assertBossControlSender, + bossCapabilityAdvertisement, + bossControlKind, + exactBossSessionTarget, + hasAuthoritativeBossControlCorrelation, + parseExactRegistrationFrame, + parseBossParticipantRegistrationMetadata, +} from "./boss-adapter.ts"; import { BrokerAuditLog } from "./audit.ts"; +import { + BossControlResultLedger, + bossControlAcceptedRecoveryFrames, + bossControlReplayFrames, + type BossControlResult, +} from "./boss-control-ledger.ts"; import type { AskCancellationReason, BrokerErrorCode, @@ -33,6 +51,7 @@ import type { DeliveryFailureCode, Message, Attachment, + BossControlFailureCode, SessionInfo, SessionRegistration, RemotePrincipalSummary, @@ -48,6 +67,7 @@ const ASK_STATE_PATH = getBrokerAskStateFilePath(INTERCOM_DIR); const ACCESS_STATE_PATH = getBrokerAccessStateFilePath(INTERCOM_DIR); const ADMIN_CREDENTIAL_PATH = getBrokerAdminCredentialFilePath(INTERCOM_DIR); const AUDIT_PATH = getBrokerAuditFilePath(INTERCOM_DIR); +const BOSS_CONTROL_LEDGER_PATH = join(INTERCOM_DIR, "boss-control-results.json"); const BROKER_STATE_ID = randomUUID(); const MAX_SESSIONS = 128; const MAX_UNREGISTERED_CONNECTIONS = 32; @@ -126,6 +146,20 @@ interface PendingDelivery { timeout: NodeJS.Timeout; } +interface PendingBossControl { + deliveryId: string; + key: string; + fingerprint: string; + requestId: string; + messageId: string; + envelope: BossControlEnvelope; + from: string; + to: string; + senderSocket: net.Socket; + recipientSocket: net.Socket; + timeout: NodeJS.Timeout; +} + interface RecentDelivery { fingerprint: string; from: string; @@ -240,6 +274,14 @@ function isSessionRegistration(value: unknown): value is SessionRegistration { return false; } + if (session.boss !== undefined) { + try { + parseBossParticipantRegistrationMetadata(session.boss); + } catch { + return false; + } + } + if (session.name !== undefined && (typeof session.name !== "string" || session.name.length > MAX_SESSION_NAME_LENGTH)) { return false; } @@ -273,6 +315,8 @@ class IntercomBroker { private pendingDeliveries = new Map(); private pendingDeliveryKeys = new Map(); private recentDeliveries = new Map(); + private pendingBossControls = new Map(); + private pendingBossControlKeys = new Map(); private connections = new Set(); private unregisteredConnections = new Set(); private server: net.Server; @@ -282,12 +326,14 @@ class IntercomBroker { private readonly askTimeoutMs = getAskTimeoutMs(); private readonly accessRegistry: RemoteAccessRegistry; private readonly audit: BrokerAuditLog; + private readonly bossControlLedger: BossControlResultLedger; constructor() { ensureIntercomRuntimeDir(INTERCOM_DIR); acquireBrokerOwnership(OWNER_PATH); this.accessRegistry = new RemoteAccessRegistry(ACCESS_STATE_PATH); this.audit = new BrokerAuditLog(AUDIT_PATH); + this.bossControlLedger = new BossControlResultLedger(BOSS_CONTROL_LEDGER_PATH); this.accessRegistry.ensureAdminCredential(ADMIN_CREDENTIAL_PATH); this.loadAskEdges(); if (typeof LISTEN_TARGET === "string" && process.platform !== "win32") { @@ -492,6 +538,38 @@ class IntercomBroker { writeMessage(socket, { type: "delivery_failed", messageId, accepted, code, reason }); } + private sendBossControlFailure( + socket: net.Socket, + requestId: string, + messageId: string, + idempotencyKey: string, + code: BossControlFailureCode, + reason: string, + deliveryId?: string, + ledgerBinding?: { scope: string; fingerprint: string }, + acknowledgeAcceptedRecovery = false, + ): void { + const result: BossControlResult = { + type: "boss_control_result", + requestId, + messageId, + idempotencyKey, + status: "rejected", + delivered: false, + code, + reason, + ...(deliveryId === undefined ? {} : { deliveryId }), + }; + if (ledgerBinding) { + this.bossControlLedger.recordTerminal(ledgerBinding.scope, ledgerBinding.fingerprint, result); + } + if (acknowledgeAcceptedRecovery) { + for (const frame of bossControlAcceptedRecoveryFrames(result)) writeMessage(socket, frame); + } else { + writeMessage(socket, result); + } + } + private scheduleShutdownCheck(): void { if (this.shutdownTimer) return; @@ -511,9 +589,16 @@ class IntercomBroker { currentId: string | null, setId: (id: string | null) => void, ): void { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes.isProxy(msg)) { throw new Error("Invalid client message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if ( + typeDescriptor === undefined + || !typeDescriptor.enumerable + || !Object.hasOwn(typeDescriptor, "value") + || typeof typeDescriptor.value !== "string" + ) throw new Error("Invalid client message"); const clientMessage = msg as { type: string } & Record; const requiresEndpointAuth = typeof LISTEN_TARGET !== "string"; @@ -533,6 +618,9 @@ class IntercomBroker { version: INTERCOM_PROTOCOL_VERSION, endpoint: origin, remoteAccess: this.remoteAccessContract(), + ...(bossCapabilityAdvertisement() === undefined + ? {} + : { capabilities: bossCapabilityAdvertisement() }), }); return; } @@ -563,8 +651,17 @@ class IntercomBroker { switch (clientMessage.type) { case "register": { + try { + parseExactRegistrationFrame(clientMessage); + } catch (error) { + this.sendError(socket, "BOSS_CONTRACT_MISMATCH", error instanceof Error ? error.message : "Registration contract is invalid"); + socket.end(); + break; + } if (!isSessionRegistration(clientMessage.session)) { - throw new Error("Invalid register message"); + this.sendError(socket, "BOSS_CONTRACT_MISMATCH", "Registration session contract is invalid"); + socket.end(); + break; } if ( @@ -583,6 +680,22 @@ class IntercomBroker { if (currentId) { throw new Error("Received duplicate register message"); } + + if (clientMessage.session.boss !== undefined) { + // Stage B publishes the exact registration contract without + // activating it on the legacy user-owned broker. No credential is + // consumed and no binding is trusted before the protected service + // predicates make an advertisement possible. + if (origin !== "local") { + this.sendError(socket, "ACCESS_DENIED", "Boss participants require the protected local broker endpoint"); + } else if (bossCapabilityAdvertisement() === undefined) { + this.sendError(socket, "BOSS_FEATURE_UNAVAILABLE", "boss-run-v1 is not advertised by this broker"); + } else { + this.sendError(socket, "BOSS_FEATURE_UNAVAILABLE", "Boss participant credential binding is not installed"); + } + socket.end(); + break; + } let id: string; let remotePrincipal: RemotePrincipalRecord | undefined; @@ -1002,6 +1115,162 @@ class IntercomBroker { break; } + case "boss_control": { + if (!currentId) throw new Error("Received boss_control before register"); + const requestId = clientMessage.requestId; + const requestedTarget = clientMessage.to; + if ( + typeof requestId !== "string" + || requestId.length === 0 + || requestId.length > MAX_MESSAGE_ID_LENGTH + || typeof requestedTarget !== "string" + || requestedTarget.length === 0 + || requestedTarget.length > MAX_TARGET_LENGTH + ) throw new Error("Invalid boss_control routing metadata"); + + const sender = this.sessions.get(currentId); + let envelope: BossControlEnvelope; + let controlKind: ReturnType["controlKind"]; + try { + if (!sender || sender.socket !== socket) throw new Error("Boss sender session not found"); + envelope = assertBossControlSender(sender.info, clientMessage.envelope); + controlKind = bossControlKind(envelope).controlKind; + if (requestId !== envelope.messageId) { + throw new Error("requestId must equal the canonical Boss envelope messageId"); + } + } catch (error) { + // The envelope may be an outer or nested Proxy rejected by the + // adapter preflight. Never inspect it again on the failure path. + this.sendBossControlFailure( + socket, + requestId, + requestId, + requestId, + "INVALID_CONTROL", + error instanceof Error ? error.message : "Invalid Boss control envelope", + ); + break; + } + + const key = this.bossControlKey(currentId, envelope); + const fingerprint = this.bossControlFingerprint(requestedTarget, envelope); + const prior = this.bossControlLedger.lookup(key, fingerprint); + if (prior.status === "replay") { + for (const frame of bossControlReplayFrames(prior.result, envelope.messageId)) writeMessage(socket, frame); + break; + } + if (prior.status === "conflict") { + this.sendBossControlFailure( + socket, + requestId, + envelope.messageId, + envelope.idempotencyKey, + "IDEMPOTENCY_CONFLICT", + "Boss idempotency key is durably bound to a different canonical request", + ); + break; + } + + // Boss routing is identity-bearing: names and ID prefixes are never + // accepted. The protected provider must supply durable causation + // evidence before this dormant adapter may claim correlation. + const exactTarget = exactBossSessionTarget(this.sessions, requestedTarget); + const correlated = hasAuthoritativeBossControlCorrelation(); + const target = exactTarget && authorizeSessionAction( + Array.from(this.sessions.values(), (session) => session.info), + currentId, + "control", + exactTarget.info.id, + { controlKind, correlated }, + ).allowed ? exactTarget : undefined; + if (!target) { + const acceptedDeliveryId = prior.status === "accepted" ? prior.deliveryId : undefined; + this.sendBossControlFailure( + socket, + requestId, + envelope.messageId, + envelope.idempotencyKey, + exactTarget ? "POLICY_DENIED" : "SESSION_NOT_FOUND", + exactTarget + ? "Boss control routing requires authoritative correlation evidence" + : "Boss control target session ID was not found", + acceptedDeliveryId, + { scope: key, fingerprint }, + acceptedDeliveryId !== undefined, + ); + break; + } + const existingDeliveryId = this.pendingBossControlKeys.get(key); + if (existingDeliveryId) { + const existing = this.pendingBossControls.get(existingDeliveryId); + if (!existing || existing.fingerprint !== fingerprint) { + this.sendBossControlFailure(socket, requestId, envelope.messageId, envelope.idempotencyKey, "IDEMPOTENCY_CONFLICT", "Boss idempotency key is already bound to a different canonical request"); + break; + } + if (existing.messageId !== envelope.messageId) { + existing.requestId = requestId; + existing.messageId = envelope.messageId; + existing.envelope = envelope; + existing.senderSocket = socket; + writeMessage(existing.recipientSocket, { type: "boss_control", deliveryId: existing.deliveryId, from: sender.info, envelope }); + } + writeMessage(socket, { + type: "boss_control_ack", + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "accepted", + deliveryId: existing.deliveryId, + }); + break; + } + const deliveryId = prior.status === "accepted" ? prior.deliveryId : randomUUID(); + if (prior.status === "miss") { + this.bossControlLedger.recordAccepted(key, fingerprint, deliveryId); + } + const timeout = setTimeout(() => { + this.failPendingBossControl(deliveryId, "DELIVERY_TIMEOUT", "Recipient did not acknowledge the Boss control envelope in time"); + }, DELIVERY_ACK_TIMEOUT_MS); + timeout.unref?.(); + this.pendingBossControls.set(deliveryId, { + deliveryId, + key, + fingerprint, + requestId, + messageId: envelope.messageId, + envelope, + from: currentId, + to: target.info.id, + senderSocket: socket, + recipientSocket: target.socket, + timeout, + }); + this.pendingBossControlKeys.set(key, deliveryId); + writeMessage(socket, { + type: "boss_control_ack", + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "accepted", + deliveryId, + }); + writeMessage(target.socket, { type: "boss_control", deliveryId, from: sender.info, envelope }); + break; + } + + case "boss_control_received": { + if (!currentId) throw new Error("Received boss_control_received before register"); + if ( + typeof clientMessage.deliveryId !== "string" + || typeof clientMessage.messageId !== "string" + || typeof clientMessage.idempotencyKey !== "string" + ) { + throw new Error("Invalid boss_control_received message"); + } + this.acknowledgePendingBossControl(clientMessage.deliveryId, clientMessage.messageId, clientMessage.idempotencyKey, currentId, socket); + break; + } + case "message_received": { if (!currentId) { throw new Error("Received message_received before register"); @@ -1504,13 +1773,19 @@ class IntercomBroker { } } - private isAuthorized(actorId: string, action: PolicyAction, targetId: string): boolean { + private isAuthorized( + actorId: string, + action: PolicyAction | BossPolicyAction, + targetId: string, + bossContext?: BossAuthorizationContext, + ): boolean { if (!this.isCurrentPrincipal(actorId) || !this.isCurrentPrincipal(targetId)) return false; return authorizeSessionAction( Array.from(this.sessions.values(), (session) => session.info), actorId, action, targetId, + bossContext, ).allowed; } @@ -1739,6 +2014,109 @@ class IntercomBroker { return count; } + private bossControlKey(fromSessionId: string, envelope: BossControlEnvelope): string { + return canonicalHash("agent-intercom-codex/boss-control/idempotency-scope/v1", { + fromSessionId, + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey, + }); + } + + private bossControlFingerprint(toSessionId: string, envelope: BossControlEnvelope): string { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/request/v1", { toSessionId, envelope: stableEnvelope }); + } + + private acknowledgePendingBossControl( + deliveryId: string, + messageId: string, + idempotencyKey: string, + sessionId: string, + socket: net.Socket, + ): void { + const pending = this.pendingBossControls.get(deliveryId); + if ( + !pending + || pending.to !== sessionId + || pending.recipientSocket !== socket + || pending.messageId !== messageId + || pending.envelope.idempotencyKey !== idempotencyKey + ) return; + const sender = this.sessions.get(pending.from); + const recipient = this.sessions.get(pending.to); + const { controlKind } = bossControlKind(pending.envelope); + if ( + !sender + || !recipient + || !authorizeSessionAction( + Array.from(this.sessions.values(), (session) => session.info), + pending.from, + "control", + pending.to, + { controlKind, correlated: hasAuthoritativeBossControlCorrelation() }, + ).allowed + ) { + this.failPendingBossControl(deliveryId, "POLICY_DENIED", "Boss control authorization changed before acknowledgement"); + return; + } + if (sender.socket === pending.senderSocket) { + const result: BossControlResult = { + type: "boss_control_result", + requestId: pending.requestId, + messageId: pending.messageId, + idempotencyKey: pending.envelope.idempotencyKey, + status: "delivered", + deliveryId, + delivered: true, + }; + this.bossControlLedger.recordTerminal(pending.key, pending.fingerprint, result); + clearTimeout(pending.timeout); + this.pendingBossControls.delete(deliveryId); + this.pendingBossControlKeys.delete(pending.key); + writeMessage(sender.socket, result); + } + } + + private failPendingBossControl( + deliveryId: string, + code: BossControlFailureCode, + reason: string, + ): void { + const pending = this.pendingBossControls.get(deliveryId); + if (!pending) return; + const sender = this.sessions.get(pending.from); + const result: BossControlResult = { + type: "boss_control_result", + requestId: pending.requestId, + messageId: pending.messageId, + idempotencyKey: pending.envelope.idempotencyKey, + status: "rejected", + delivered: false, + code, + reason, + deliveryId, + }; + this.bossControlLedger.recordTerminal(pending.key, pending.fingerprint, result); + clearTimeout(pending.timeout); + this.pendingBossControls.delete(deliveryId); + this.pendingBossControlKeys.delete(pending.key); + if (sender?.socket === pending.senderSocket) writeMessage(sender.socket, result); + } + + private clearPendingBossControlsForSession(sessionId: string, socket: net.Socket): void { + for (const pending of Array.from(this.pendingBossControls.values())) { + if (pending.to === sessionId && pending.recipientSocket === socket) { + this.failPendingBossControl(pending.deliveryId, "RECIPIENT_DISCONNECTED", "Boss control recipient disconnected before acknowledgement"); + } else if (pending.from === sessionId && pending.senderSocket === socket) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(pending.deliveryId); + this.pendingBossControlKeys.delete(pending.key); + } + } + } + private acknowledgePendingDelivery(deliveryId: string, sessionId: string, socket: net.Socket): void { const pending = this.pendingDeliveries.get(deliveryId); if (!pending || pending.to !== sessionId || pending.recipientSocket !== socket) { @@ -1817,6 +2195,7 @@ class IntercomBroker { } private clearPendingDeliveriesForSession(sessionId: string, socket: net.Socket): void { + this.clearPendingBossControlsForSession(sessionId, socket); for (const delivery of Array.from(this.pendingDeliveries.values())) { if (delivery.to === sessionId && delivery.recipientSocket === socket) { this.failPendingDelivery(delivery.id, "RECIPIENT_DISCONNECTED", "Recipient disconnected before acknowledging the message"); @@ -1867,6 +2246,11 @@ class IntercomBroker { } this.pendingDeliveries.clear(); this.pendingDeliveryKeys.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + } + this.pendingBossControls.clear(); + this.pendingBossControlKeys.clear(); for (const edge of this.askEdges.values()) { clearTimeout(edge.timeout); } diff --git a/broker/client.test.ts b/broker/client.test.ts index f372e7f..aa4c585 100644 --- a/broker/client.test.ts +++ b/broker/client.test.ts @@ -1,6 +1,11 @@ import test from "node:test"; import assert from "node:assert/strict"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { BOSS_CONTROL_ENVELOPE_VERSION } from "@dataforxyz/agent-intercom-core/boss"; import { IntercomClient } from "./client.ts"; +import { PersistentBossControlOutbox } from "../boss-control-outbox.ts"; test("cancelAsk resolves false after synchronous socket write failures", async () => { const client = new IntercomClient(); @@ -16,3 +21,204 @@ test("cancelAsk resolves false after synchronous socket write failures", async ( assert.equal(await client.cancelAsk("ask-1"), false); }); + +test("client registration preflight is exact and rejects proxies with zero traps before connect", async () => { + const client = new IntercomClient(); + let trapCount = 0; + const proxy = new Proxy({}, { + get() { trapCount += 1; throw new Error("trap"); }, + ownKeys() { trapCount += 1; throw new Error("trap"); }, + getOwnPropertyDescriptor() { trapCount += 1; throw new Error("trap"); }, + getPrototypeOf() { trapCount += 1; throw new Error("trap"); }, + }); + await assert.rejects(client.connect(proxy as never), /proxies are not supported/); + assert.equal(trapCount, 0); + await assert.rejects(client.connect({ + cwd: "/tmp", + model: "gpt", + pid: 1, + startedAt: 1, + lastActivity: 1, + capabilities: {}, + } as never), /not supported/); +}); + +test("caller and inbound Boss envelopes reject outer and nested proxies with zero traps before Core parsing", async () => { + const client = new IntercomClient() as any; + client._sessionId = "session-1"; + client.socket = { destroyed: false, writableEnded: false, writable: true }; + const from = { id: "sender", cwd: "/tmp", model: "gpt", pid: 1, startedAt: 1, lastActivity: 1 }; + + for (const nested of [false, true]) { + let trapCount = 0; + const proxy = new Proxy({}, { + get() { trapCount += 1; throw new Error("trap"); }, + ownKeys() { trapCount += 1; throw new Error("trap"); }, + getOwnPropertyDescriptor() { trapCount += 1; throw new Error("trap"); }, + getPrototypeOf() { trapCount += 1; throw new Error("trap"); }, + }); + const envelope = nested ? { + type: "boss.worker.health", + version: BOSS_CONTROL_ENVELOPE_VERSION, + messageId: "message-proxy", + bossRunId: "run-1", + participantId: "worker-1", + bindingEpoch: 1, + idempotencyKey: "operation-proxy", + payload: proxy, + } : proxy; + + await assert.rejects(client.sendBossControl("target", envelope), /proxies are not supported/); + assert.equal(trapCount, 0, `${nested ? "nested" : "outer"} caller proxy must remain untouched`); + assert.throws( + () => client.handleBrokerMessage({ type: "boss_control", deliveryId: "delivery-proxy", from, envelope }), + /proxies are not supported/, + ); + assert.equal(trapCount, 0, `${nested ? "nested" : "outer"} inbound proxy must remain untouched`); + } +}); + +test("client Boss response state machine accepts only an identical replay ACK and rejects changed correlation", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-client-order-")); + try { + const client = new IntercomClient() as any; + client._sessionId = "session-1"; + client.bossControlOutbox = new PersistentBossControlOutbox("session-1", dir); + const envelope = { + type: "boss.worker.health", + version: BOSS_CONTROL_ENVELOPE_VERSION, + messageId: "message-1", + bossRunId: "run-1", + participantId: "worker-1", + bindingEpoch: 1, + idempotencyKey: "operation-1", + payload: { state: "working" }, + }; + client.bossControlOutbox.enqueue("target-1", envelope); + const timeout = setTimeout(() => undefined, 60_000); + timeout.unref(); + client.pendingBossControls.set("message-1", { + messageId: "message-1", + idempotencyKey: "operation-1", + resolve() {}, + reject() {}, + timeout, + }); + const result = { + type: "boss_control_result", + requestId: "message-1", + messageId: "message-1", + idempotencyKey: "operation-1", + status: "delivered", + delivered: true, + deliveryId: "delivery-1", + }; + assert.throws(() => client.handleBrokerMessage(result), /before the matching durable acknowledgement/); + assert.equal(client.bossControlOutbox.list().length, 1); + const ack = { + type: "boss_control_ack", + requestId: "message-1", + messageId: "message-1", + idempotencyKey: "operation-1", + status: "accepted", + deliveryId: "delivery-1", + }; + client.handleBrokerMessage(ack); + assert.doesNotThrow(() => client.handleBrokerMessage(ack)); + assert.throws( + () => client.handleBrokerMessage({ ...ack, deliveryId: "delivery-2" }), + /changed the durable deliveryId/, + ); + assert.throws( + () => client.handleBrokerMessage({ ...ack, requestId: "message-2", messageId: "message-2" }), + /does not match the durable outbox binding/, + ); + assert.throws( + () => client.handleBrokerMessage({ ...ack, idempotencyKey: "operation-2" }), + /correlation does not match the pending request/, + ); + client.pendingBossControls.clear(); + assert.doesNotThrow( + () => client.handleBrokerMessage(ack), + "a reconnect without pending memory must accept the exact durable replay ACK", + ); + assert.throws(() => client.handleBrokerMessage({ ...result, deliveryId: "delivery-2" }), /matching durable acknowledgement/); + assert.equal(client.bossControlOutbox.list().length, 1); + client.handleBrokerMessage(result); + assert.deepEqual(client.bossControlOutbox.list(), []); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("a reconnected caller consumes replayed ACK then terminal and clears its durable outbox", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-client-reconnect-terminal-")); + try { + const durable = new PersistentBossControlOutbox("session-1", dir); + durable.enqueue("target-1", { + type: "boss.worker.health", + version: BOSS_CONTROL_ENVELOPE_VERSION, + messageId: "message-1", + bossRunId: "run-1", + participantId: "worker-1", + bindingEpoch: 1, + idempotencyKey: "operation-1", + payload: { state: "working" }, + }); + durable.markAccepted("operation-1", "message-1", "delivery-1"); + + const reconnected = new IntercomClient() as any; + reconnected._sessionId = "session-1"; + reconnected.bossControlOutbox = new PersistentBossControlOutbox("session-1", dir); + assert.doesNotThrow(() => reconnected.handleBrokerMessage({ + type: "boss_control_ack", + requestId: "message-1", + messageId: "message-1", + idempotencyKey: "operation-1", + status: "accepted", + deliveryId: "delivery-1", + })); + assert.doesNotThrow(() => reconnected.handleBrokerMessage({ + type: "boss_control_result", + requestId: "message-1", + messageId: "message-1", + idempotencyKey: "operation-1", + status: "delivered", + delivered: true, + deliveryId: "delivery-1", + })); + assert.deepEqual(reconnected.bossControlOutbox.list(), []); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("a reconstructed caller accepts its one first ACK when the durable outbox is still queued", () => { + const dir = mkdtempSync(join(tmpdir(), "boss-client-reconstructed-")); + try { + const client = new IntercomClient() as any; + client._sessionId = "session-1"; + client.bossControlOutbox = new PersistentBossControlOutbox("session-1", dir); + client.bossControlOutbox.enqueue("target-1", { + type: "boss.worker.health", + version: BOSS_CONTROL_ENVELOPE_VERSION, + messageId: "message-1", + bossRunId: "run-1", + participantId: "worker-1", + bindingEpoch: 1, + idempotencyKey: "operation-1", + payload: { state: "working" }, + }); + assert.doesNotThrow(() => client.handleBrokerMessage({ + type: "boss_control_ack", + requestId: "message-1", + messageId: "message-1", + idempotencyKey: "operation-1", + status: "accepted", + deliveryId: "delivery-1", + })); + assert.equal(client.bossControlOutbox.list()[0].state, "accepted"); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/broker/client.ts b/broker/client.ts index c96d794..461da47 100644 --- a/broker/client.ts +++ b/broker/client.ts @@ -2,9 +2,24 @@ import { EventEmitter } from "events"; import net from "net"; import { randomUUID } from "crypto"; import { POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION } from "@dataforxyz/agent-intercom-core"; +import { + BOSS_RUN_FEATURE, + parseBrokerCapabilityAdvertisement, + type BossControlEnvelope, +} from "@dataforxyz/agent-intercom-core/boss"; import { writeMessage, createMessageReader } from "./framing.ts"; import { PersistentOutboundOutbox } from "../outbound-outbox.ts"; +import { PersistentBossControlOutbox } from "../boss-control-outbox.ts"; import { loadRemoteAccessCredential, writeRemoteSessionCredential, type LoadedRemoteAccessCredential } from "./access-credential.ts"; +import { parseBossControlAck, parseBossControlResult } from "./boss-control-ledger.ts"; +import { + parseBossParticipantBindingMetadata, + parseBossParticipantRegistrationMetadata, + bossControlKind, + parseExactRegisteredFrame, + parseExactRegistrationFrame, +} from "./boss-adapter.ts"; +import { types as nodeUtilTypes } from "node:util"; import { getBrokerConnectTarget, INTERCOM_PROTOCOL_NAME, @@ -18,6 +33,9 @@ import type { Message, Attachment, SessionRegistration, + BossParticipantBindingMetadata, + BossParticipantRegistrationMetadata, + BossControlFailureCode, } from "../types.ts"; export interface SendOptions { @@ -37,6 +55,24 @@ export interface SendResult { reason?: string; } +export type BossControlSendResult = { + requestId: string; + messageId: string; + idempotencyKey: string; + status: "delivered"; + delivered: true; + deliveryId: string; +} | { + requestId: string; + messageId: string; + idempotencyKey: string; + status: "rejected"; + delivered: false; + deliveryId?: string; + code: BossControlFailureCode; + reason: string; +}; + function toError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)); } @@ -141,6 +177,13 @@ function isSessionInfo(value: unknown): value is SessionInfo { for (const field of ["depth", "maxDepth", "maxChildren"] as const) { if (session[field] !== undefined && (typeof session[field] !== "number" || !Number.isSafeInteger(session[field]))) return false; } + if (session.boss !== undefined) { + try { + parseBossParticipantBindingMetadata(session.boss, session.id); + } catch { + return false; + } + } return true; } @@ -175,8 +218,19 @@ export class IntercomClient extends EventEmitter { }>(); private pendingLists = new Map void; reject: (e: Error) => void }>(); private pendingAskControls = new Map void; timeout: NodeJS.Timeout }>(); + private pendingBossControls = new Map void; + reject: (error: Error) => void; + timeout: NodeJS.Timeout; + }>(); private outbox: PersistentOutboundOutbox | null = null; + private bossControlOutbox: PersistentBossControlOutbox | null = null; private remoteAccessCredential: LoadedRemoteAccessCredential | undefined; + private requestedBossRegistration: BossParticipantRegistrationMetadata | undefined; + private _bossBinding: BossParticipantBindingMetadata | undefined; private disconnecting = false; private disconnectError: Error | null = null; @@ -194,6 +248,11 @@ export class IntercomClient extends EventEmitter { pending.resolve(false); } this.pendingAskControls.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + pending.reject(error); + } + this.pendingBossControls.clear(); } get sessionId(): string | null { @@ -204,6 +263,14 @@ export class IntercomClient extends EventEmitter { return this.outbox?.list().length ?? 0; } + get bossBinding(): BossParticipantBindingMetadata | undefined { + return this._bossBinding; + } + + get bossControlOutboxSize(): number { + return this.bossControlOutbox?.list().length ?? 0; + } + isConnected(): boolean { const socket = this.socket; return Boolean(socket && this._sessionId && !this.disconnecting && !socket.destroyed && !socket.writableEnded && socket.writable); @@ -231,6 +298,25 @@ export class IntercomClient extends EventEmitter { return Promise.reject(new Error("Already connected")); } + try { + const canonicalSession = parseExactRegistrationFrame({ + type: "register", + ...(typeof session === "object" && session !== null && !nodeUtilTypes.isProxy(session) + && Object.getOwnPropertyDescriptor(session, "boss") !== undefined + ? { registrationKind: "boss" as const } + : {}), + protocol: INTERCOM_PROTOCOL_NAME, + version: INTERCOM_PROTOCOL_VERSION, + session, + }).session; + this.requestedBossRegistration = session.boss === undefined + ? undefined + : parseBossParticipantRegistrationMetadata(session.boss); + if (canonicalSession !== session) throw new Error("Registration session identity changed during validation"); + } catch (error) { + return Promise.reject(toError(error)); + } + return new Promise((resolve, reject) => { let socket: net.Socket; let target: BrokerConnectTarget; @@ -289,6 +375,8 @@ export class IntercomClient extends EventEmitter { this.socket = null; } this._sessionId = null; + this._bossBinding = undefined; + this.requestedBossRegistration = undefined; this.disconnectError = null; if (connectionEstablished && !wasDisconnecting) { this.emit("disconnected", disconnectError); @@ -342,6 +430,7 @@ export class IntercomClient extends EventEmitter { try { writeMessage(socket, { type: "register", + ...(session.boss === undefined ? {} : { registrationKind: "boss" as const }), protocol: INTERCOM_PROTOCOL_NAME, version: INTERCOM_PROTOCOL_VERSION, session, @@ -362,9 +451,16 @@ export class IntercomClient extends EventEmitter { } private handleBrokerMessage(msg: unknown): void { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes.isProxy(msg)) { throw new Error("Invalid broker message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if ( + typeDescriptor === undefined + || !typeDescriptor.enumerable + || !Object.hasOwn(typeDescriptor, "value") + || typeof typeDescriptor.value !== "string" + ) throw new Error("Invalid broker message"); const brokerMessage = msg as { type: string } & Record; @@ -374,6 +470,12 @@ export class IntercomClient extends EventEmitter { switch (brokerMessage.type) { case "registered": { + parseExactRegisteredFrame( + brokerMessage, + this.requestedBossRegistration === undefined + ? this.remoteAccessCredential === undefined ? "ordinary-local" : "ordinary-remote" + : "boss", + ); if ( typeof brokerMessage.sessionId !== "string" || brokerMessage.protocol !== INTERCOM_PROTOCOL_NAME @@ -412,9 +514,39 @@ export class IntercomClient extends EventEmitter { } } + + if (this.requestedBossRegistration !== undefined) { + if (brokerMessage.remoteAccess !== undefined || brokerMessage.access !== undefined) { + throw new Error("Boss registration returned folded remote-access metadata"); + } + const advertisement = parseBrokerCapabilityAdvertisement(brokerMessage.capabilities); + if (!advertisement.features.some((feature) => feature.feature === BOSS_RUN_FEATURE)) { + throw new Error("Broker did not echo the required boss-run-v1 feature contract"); + } + const binding = parseBossParticipantBindingMetadata(brokerMessage.boss, brokerMessage.sessionId); + const credential = this.requestedBossRegistration.credential; + if ( + binding.featureContract.feature !== this.requestedBossRegistration.featureContract.feature + || binding.binding.bossRunId !== credential.bossRunId + || binding.binding.participantId !== credential.participantId + || binding.binding.role !== credential.role + || binding.binding.communicationProfile !== credential.communicationProfile + || binding.binding.bindingEpoch !== credential.bindingEpoch + ) { + throw new Error("Broker returned a Boss binding that does not match the authenticated registration request"); + } + this._bossBinding = binding; + } else if (brokerMessage.boss !== undefined) { + throw new Error("Broker attached unsolicited Boss binding metadata to an ordinary registration"); + } + this._sessionId = brokerMessage.sessionId; this.outbox = new PersistentOutboundOutbox(brokerMessage.sessionId); + this.bossControlOutbox = this._bossBinding === undefined + ? null + : new PersistentBossControlOutbox(brokerMessage.sessionId); this.replayOutbox(); + this.replayBossControlOutbox(); this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId }); break; } @@ -446,6 +578,64 @@ export class IntercomClient extends EventEmitter { break; } + case "boss_control": { + const { deliveryId, from } = brokerMessage; + if (typeof deliveryId !== "string" || !isSessionInfo(from)) { + throw new Error("Invalid boss_control event"); + } + // Keep every inbound envelope behind the adapter's recursive, + // proxy-zero-trap preflight before Core parses or reflects on it. + const envelope = bossControlKind(brokerMessage.envelope).envelope; + const source = from.boss === undefined + ? undefined + : parseBossParticipantBindingMetadata(from.boss, from.id).binding; + if ( + source === undefined + || source.state !== "active" + || source.bossRunId !== envelope.bossRunId + || source.participantId !== envelope.participantId + || source.bindingEpoch !== envelope.bindingEpoch + ) throw new Error("Boss control event sender does not match its broker-owned binding"); + this.emit("boss_control", from, envelope, deliveryId); + break; + } + + case "boss_control_result": { + const result = parseBossControlResult(brokerMessage); + const { requestId, messageId, idempotencyKey, deliveryId } = result; + const stored = this.bossControlOutbox?.find(idempotencyKey); + if (!stored || stored.envelope.messageId !== requestId) throw new Error("Boss control result does not match the durable outbox binding"); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control result correlation does not match the pending request"); + } + this.bossControlOutbox!.removeCorrelated(idempotencyKey, messageId, deliveryId); + if (pending) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(requestId); + pending.resolve(result); + } + break; + } + + case "boss_control_ack": { + const { requestId, messageId, idempotencyKey, deliveryId } = parseBossControlAck(brokerMessage); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control acknowledgement correlation does not match the pending request"); + } + const transition = this.bossControlOutbox?.markAccepted(idempotencyKey, messageId, deliveryId); + if (transition === undefined) throw new Error("Boss control acknowledgement has no durable outbox"); + // A terminal replay is deliberately ACK-first. The exact ACK is + // idempotent when this durable accepted binding survived reconnect; + // markAccepted still rejects any changed correlation or deliveryId. + if (pending?.deliveryId !== undefined && pending.deliveryId !== deliveryId) { + throw new Error("Boss control acknowledgement changed the pending deliveryId"); + } + if (pending) pending.deliveryId = deliveryId; + break; + } + case "delivery_accepted": { const { deliveryId, messageId } = brokerMessage; if (typeof deliveryId !== "string" || typeof messageId !== "string") { @@ -739,6 +929,74 @@ export class IntercomClient extends EventEmitter { }); } + sendBossControl(to: string, envelopeValue: unknown): Promise { + let socket: net.Socket; + try { + socket = this.requireActiveSocket(); + } catch (error) { + return Promise.reject(toError(error)); + } + let envelope: BossControlEnvelope; + try { + // Caller objects receive the same adapter-owned zero-trap boundary as + // broker-delivered envelopes; Core never sees a Proxy at any depth. + envelope = bossControlKind(envelopeValue).envelope; + const binding = this._bossBinding?.binding; + if ( + binding === undefined + || binding.state !== "active" + || envelope.bossRunId !== binding.bossRunId + || envelope.participantId !== binding.participantId + || envelope.bindingEpoch !== binding.bindingEpoch + ) throw new Error("Boss control envelope does not match this client's active participant binding"); + } catch (error) { + return Promise.reject(toError(error)); + } + const requestId = envelope.messageId; + if (this.pendingBossControls.has(requestId)) { + return Promise.resolve({ + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "rejected", + delivered: false, + code: "INVALID_CONTROL", + reason: "Boss requestId is already pending", + }); + } + try { + if (!this.bossControlOutbox) throw new Error("Durable Boss control outbox is unavailable"); + this.bossControlOutbox.enqueue(to, envelope); + } catch (error) { + return Promise.reject(toError(error)); + } + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + if (!this.pendingBossControls.delete(requestId)) return; + reject(new Error("Boss control delivery timeout")); + }, 10000); + timeout.unref?.(); + this.pendingBossControls.set(requestId, { + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + resolve, + reject, + timeout, + }); + try { + writeMessage(socket, { type: "boss_control", requestId, to, envelope }); + } catch (error) { + clearTimeout(timeout); + this.pendingBossControls.delete(requestId); + reject(toError(error)); + } + }); + } + + acknowledgeBossControl(deliveryId: string, messageId: string, idempotencyKey: string): boolean { + return this.writeControlMessage({ type: "boss_control_received", deliveryId, messageId, idempotencyKey }); + } + acknowledgeMessage(deliveryId: string): boolean { return this.writeControlMessage({ type: "message_received", deliveryId }); } @@ -804,6 +1062,23 @@ export class IntercomClient extends EventEmitter { } } + private replayBossControlOutbox(): void { + const socket = this.socket; + if (!socket || socket.destroyed || !this._sessionId || !this.bossControlOutbox) return; + for (const entry of this.bossControlOutbox.list()) { + try { + writeMessage(socket, { + type: "boss_control", + requestId: entry.envelope.messageId, + to: entry.to, + envelope: entry.envelope, + }); + } catch { + return; + } + } + } + updatePresence(updates: { name?: string; status?: string; model?: string }): void { if (this.disconnecting) { return; diff --git a/codex/app-server-client.test.ts b/codex/app-server-client.test.ts index 6129e8f..a669ac2 100644 --- a/codex/app-server-client.test.ts +++ b/codex/app-server-client.test.ts @@ -1,5 +1,8 @@ import test from "node:test"; import assert from "node:assert/strict"; +import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { CodexAppServerClient, defaultServerRequestResponse, WebSocketFrameDecoder } from "./app-server-client.ts"; function frame(opcode: number, payload: string, fin = true): Buffer { @@ -46,6 +49,33 @@ test("app-server error notifications do not trigger Node's unhandled error event assert.deepEqual(serverError, params); }); +test("app-server client blocks protected ambient PATH and preserves ordinary explicit launch", async () => { + const dir = mkdtempSync(join(tmpdir(), "codex-provider-client-")); + const marker = join(dir, "executed"); + const hostileCodex = join(dir, "codex"); + writeFileSync(hostileCodex, `#!/bin/sh\nprintf hostile > '${marker}'\n`); + chmodSync(hostileCodex, 0o755); + const hostileEnv = { ...process.env, PATH: dir }; + try { + assert.throws( + () => new CodexAppServerClient({ env: hostileEnv }, "boss_reviewer"), + (error: unknown) => (error as { code?: unknown }).code === "PROVIDER_AUTHORITY_UNAVAILABLE", + ); + assert.equal(existsSync(marker), false); + + const ordinary = new CodexAppServerClient({ + command: "/bin/sh", + args: ["-c", 'printf ordinary > "$1"; while IFS= read -r line; do printf \'{"id":1,"result":{}}\\n\'; done', "ordinary-server", marker], + env: hostileEnv, + }); + await ordinary.connect(); + await ordinary.disconnect(); + assert.equal(existsSync(marker), true); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + test("WebSocketFrameDecoder reassembles fragmented text frames", () => { const decoder = new WebSocketFrameDecoder(); assert.deepEqual(decoder.push(frame(0x1, "hel", false)), []); diff --git a/codex/app-server-client.ts b/codex/app-server-client.ts index bf3ebc2..23a63f5 100644 --- a/codex/app-server-client.ts +++ b/codex/app-server-client.ts @@ -4,6 +4,7 @@ import { randomBytes, createHash } from "node:crypto"; import net from "node:net"; import readline from "node:readline"; import { setTimeout as delay } from "node:timers/promises"; +import { assertHardenedBossProviderAuthority, type HardenedBossClientKind } from "./boss-client.ts"; export interface JsonRpcMessage { id?: string | number | null; @@ -23,6 +24,7 @@ export interface CodexAppServerClientOptions { startDaemonCommand?: string; startDaemonArgs?: string[]; requestTimeoutMs?: number; + env?: NodeJS.ProcessEnv; } interface PendingRequest { @@ -76,8 +78,11 @@ export class CodexAppServerClient extends EventEmitter { private initialized = false; private options: Required; - constructor(options: CodexAppServerClientOptions = {}) { + constructor(options: CodexAppServerClientOptions = {}, protectedBossClient?: HardenedBossClientKind) { super(); + // Keep the provider ceiling at the lowest shared process boundary too. + // The second argument is deny-only: it cannot convey provider authority. + assertHardenedBossProviderAuthority(protectedBossClient); this.options = { command: options.command ?? "codex", args: options.args ?? ["app-server"], @@ -88,6 +93,7 @@ export class CodexAppServerClient extends EventEmitter { startDaemonCommand: options.startDaemonCommand ?? "codex", startDaemonArgs: options.startDaemonArgs ?? ["app-server", "daemon", "start"], requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, + env: options.env ?? process.env, }; } @@ -101,6 +107,7 @@ export class CodexAppServerClient extends EventEmitter { if (this.options.startDaemon) { const started = spawnSync(this.options.startDaemonCommand, this.options.startDaemonArgs, { encoding: "utf8", + env: this.options.env, stdio: ["ignore", "pipe", "pipe"], }); if (started.status !== 0) { @@ -116,7 +123,7 @@ export class CodexAppServerClient extends EventEmitter { const proc = spawn(this.options.command, this.options.args, { stdio: ["pipe", "pipe", "pipe"], - env: process.env, + env: this.options.env, }); this.proc = proc; this.rl = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity }); diff --git a/codex/boss-client.test.ts b/codex/boss-client.test.ts new file mode 100644 index 0000000..734c3f1 --- /dev/null +++ b/codex/boss-client.test.ts @@ -0,0 +1,30 @@ +import assert from "node:assert/strict"; +import { existsSync, readFileSync } from "node:fs"; +import test from "node:test"; +import * as restrictedSurface from "./boss-client.ts"; + +test("restricted Boss clients are unavailable without a protected broker-owned factory", () => { + assert.equal(restrictedSurface.BOSS_RESTRICTED_CLIENT_AVAILABILITY, "unavailable_without_protected_factory"); + assert.equal(Object.hasOwn(restrictedSurface, "BossParticipantClient"), false); + assert.equal(Object.hasOwn(restrictedSurface, "BossReviewerClient"), false); + assert.equal(Object.hasOwn(restrictedSurface, "BossRestrictedTransport"), false); +}); + +test("protected provider failure has a stable machine-readable code", () => { + assert.throws( + () => restrictedSurface.assertHardenedBossProviderAuthority("boss_reviewer"), + (error: unknown) => error instanceof restrictedSurface.ProviderAuthorityUnavailableError + && error.code === "PROVIDER_AUTHORITY_UNAVAILABLE" + && /broker-owned, artifact-attested Codex provider executable/.test(error.message), + ); + assert.doesNotThrow(() => restrictedSurface.assertHardenedBossProviderAuthority(undefined)); +}); + +test("source/build surfaces do not advertise caller-provisionable restricted authority", () => { + const source = readFileSync(new URL("./boss-client.ts", import.meta.url), "utf8"); + const build = readFileSync(new URL("../scripts/build.mjs", import.meta.url), "utf8"); + assert.doesNotMatch(source, /export class Boss(?:Participant|Reviewer)Client/); + assert.doesNotMatch(source, /interface BossRestrictedTransport/); + assert.doesNotMatch(build, /dist\/boss-client\.mjs/); + assert.equal(existsSync(new URL("../dist/boss-client.mjs", import.meta.url)), false); +}); diff --git a/codex/boss-client.ts b/codex/boss-client.ts new file mode 100644 index 0000000..64f3e86 --- /dev/null +++ b/codex/boss-client.ts @@ -0,0 +1,34 @@ +/** + * Restricted Boss operation clients are intentionally unavailable in this + * adapter release. A safe client must be provisioned by the protected broker + * from its own authenticated binding and transport; accepting either from a + * caller would let untrusted code manufacture authority. + */ +export type HardenedBossClientKind = "boss_participant" | "boss_reviewer"; + +export const HARDENED_BOSS_CODEX_DEFAULTS = Object.freeze({ + boss_participant: Object.freeze({ approvalPolicy: "untrusted", sandbox: "workspace-write" }), + boss_reviewer: Object.freeze({ approvalPolicy: "untrusted", sandbox: "read-only" }), +} as const); + +export const BOSS_RESTRICTED_CLIENT_AVAILABILITY = "unavailable_without_protected_factory" as const; + +export const PROVIDER_AUTHORITY_UNAVAILABLE = "PROVIDER_AUTHORITY_UNAVAILABLE" as const; + +export class ProviderAuthorityUnavailableError extends Error { + readonly code = PROVIDER_AUTHORITY_UNAVAILABLE; + + constructor(readonly bossClient: HardenedBossClientKind) { + super(`${PROVIDER_AUTHORITY_UNAVAILABLE}: ${bossClient} requires a broker-owned, artifact-attested Codex provider executable`); + this.name = "ProviderAuthorityUnavailableError"; + } +} + +/** + * Protected Boss launches must not resolve `codex` through caller PATH or + * accept a caller-selected executable. No broker-owned provider attestation is + * available in this adapter yet, so every production spawn remains dormant. + */ +export function assertHardenedBossProviderAuthority(bossClient: HardenedBossClientKind | undefined): void { + if (bossClient !== undefined) throw new ProviderAuthorityUnavailableError(bossClient); +} diff --git a/codex/bridge-config.test.ts b/codex/bridge-config.test.ts index f96c351..253e917 100644 --- a/codex/bridge-config.test.ts +++ b/codex/bridge-config.test.ts @@ -1,9 +1,9 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { mkdirSync, mkdtempSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; -import { defaultBridgeConfig, loadBridgeConfig, loadBridgeState, saveBridgeState } from "./bridge-config.ts"; +import { assertHardenedBossAgentConfig, assertHardenedBossBridgeConfig, bridgeAgentApprovalPolicy, bridgeAgentDefaultSandbox, defaultBridgeConfig, loadBridgeConfig, loadBridgeState, saveBridgeState } from "./bridge-config.ts"; test("defaultBridgeConfig builds one virtual worker from env", () => { const config = defaultBridgeConfig({ @@ -51,3 +51,119 @@ test("loadBridgeState and saveBridgeState persist thread ids", () => { rmSync(dir, { recursive: true, force: true }); } }); + +test("missing config and state initialization is read-only", () => { + const dir = mkdtempSync(join(tmpdir(), "codex-bridge-read-only-init-")); + try { + assert.deepEqual(readdirSync(dir), []); + loadBridgeConfig(join(dir, "missing-config.json")); + assert.deepEqual(loadBridgeState(join(dir, "missing-state.json")), { agents: {} }); + assert.deepEqual(readdirSync(dir), []); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("hardened Boss clients default safely and reject yolo-equivalent policy", () => { + const reviewer = defaultBridgeConfig({ + CODEX_INTERCOM_BRIDGE_ID: "reviewer", + CODEX_INTERCOM_BRIDGE_CWD: "/tmp", + CODEX_INTERCOM_BOSS_CLIENT: "boss_reviewer", + }).agents[0]; + assert.equal(bridgeAgentApprovalPolicy(reviewer), "untrusted"); + assert.equal(bridgeAgentDefaultSandbox(reviewer), "read-only"); + + const dir = mkdtempSync(join(tmpdir(), "codex-bridge-boss-policy-")); + try { + const path = join(dir, "config.json"); + writeFileSync(path, JSON.stringify({ + agents: [{ + id: "participant", + bossClient: "boss_participant", + approvalPolicy: "never", + sandboxPolicy: { type: "dangerFullAccess" }, + }], + })); + assert.throws(() => loadBridgeConfig(path), /cannot use danger-full-access/); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("hardened Boss bridge validates every app-server and daemon argv position and form", () => { + const agent = { + id: "reviewer", + name: "reviewer", + cwd: "/tmp", + bossClient: "boss_reviewer" as const, + sandboxPolicy: { type: "readOnly", networkAccess: false }, + }; + assert.throws( + () => assertHardenedBossBridgeConfig({ agents: [agent], statePath: "/tmp/state", appServer: { args: ["app-server", "--profile=unsafe"] } }), + /raw --profile/, + ); + assert.throws( + () => assertHardenedBossAgentConfig({ ...agent, sandboxPolicy: { type: "readOnly", networkAccess: true } }), + /networkAccess must be false/, + ); + assert.throws( + () => assertHardenedBossBridgeConfig({ agents: [agent], statePath: "/tmp/state", appServer: { command: "/attacker/codex" } }), + /caller-provided app-server commands/, + ); + assert.throws( + () => assertHardenedBossBridgeConfig({ agents: [agent], statePath: "/tmp/state", appServer: { args: ["app-server", "--", "--enable=escape"] } }), + /raw --enable/, + ); + const overrides = [ + ["--sandbox", /policy override/], ["--sandbox=workspace-write", /policy override/], + ["-s", /policy override/], ["-s=workspace-write", /policy override/], ["-sworkspace-write", /policy override/], + ["--ask-for-approval", /policy override/], ["--ask-for-approval=untrusted", /policy override/], + ["-a", /policy override/], ["-a=untrusted", /policy override/], ["-anever", /policy override/], + ["-C", /launch escape/], ["-C\/etc", /launch escape/], + ] as const; + for (const field of ["args", "startDaemonArgs"] as const) { + for (const [override, expected] of overrides) { + for (const argv of [["app-server", override], ["app-server", "--", override]]) { + assert.throws( + () => assertHardenedBossBridgeConfig({ agents: [agent], statePath: "/tmp/state", appServer: { [field]: argv } }), + expected, + `${field} must reject ${override} in ${argv.join(" ")}`, + ); + } + } + } + assert.doesNotThrow(() => assertHardenedBossBridgeConfig({ agents: [agent], statePath: "/tmp/state", appServer: { args: [], startDaemonArgs: [] } })); + assert.doesNotThrow(() => assertHardenedBossBridgeConfig({ + agents: [{ id: "ordinary", name: "ordinary", cwd: "/tmp" }], + statePath: "/tmp/state", + appServer: { args: ["app-server", "-sworkspace-write", "-anever", "-C/etc"] }, + })); +}); + +test("writable Boss participants fail closed without broker-owned assigned-root authority", () => { + const dir = mkdtempSync(join(tmpdir(), "codex-boss-root-")); + try { + const inside = join(dir, "inside"); + const rootAlias = join(dir, "root-alias"); + mkdirSync(inside); + symlinkSync("/", rootAlias); + const candidates = [ + { cwd: inside, roots: [inside] }, + { cwd: "/etc", roots: ["/etc"] }, + { cwd: "/", roots: ["/"] }, + { cwd: rootAlias, roots: [rootAlias] }, + { cwd: `${inside}/..`, roots: [`${inside}/..`] }, + ]; + for (const candidate of candidates) { + assert.throws(() => assertHardenedBossAgentConfig({ + id: "worker", + name: "worker", + cwd: candidate.cwd, + bossClient: "boss_participant", + sandboxPolicy: { type: "workspaceWrite", writableRoots: candidate.roots, networkAccess: false }, + }), /broker-owned assigned workspace authority|filesystem root/); + } + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/codex/bridge-config.ts b/codex/bridge-config.ts index 3adcbd7..2b374dd 100644 --- a/codex/bridge-config.ts +++ b/codex/bridge-config.ts @@ -1,7 +1,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; -import { dirname, join, resolve } from "node:path"; +import { dirname, join, parse as parsePath, resolve } from "node:path"; import { cwd as processCwd } from "node:process"; +import { types as nodeUtilTypes } from "node:util"; import { getIntercomDirPath, restrictIntercomRuntimeFile } from "../broker/paths.ts"; +import { HARDENED_BOSS_CODEX_DEFAULTS, type HardenedBossClientKind } from "./boss-client.ts"; +import { assertBossCanonicalData } from "../broker/boss-adapter.ts"; export interface BridgeAgentConfig { id: string; @@ -12,6 +15,7 @@ export interface BridgeAgentConfig { instructions?: string; approvalPolicy?: unknown; sandboxPolicy?: unknown; + bossClient?: HardenedBossClientKind; } export interface BridgeConfig { @@ -36,7 +40,7 @@ export const DEFAULT_BRIDGE_CONFIG_PATH = join(getIntercomDirPath(), "codex-brid export const DEFAULT_BRIDGE_STATE_PATH = join(getIntercomDirPath(), "codex-bridge-state.json"); function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); + return typeof value === "object" && value !== null && !Array.isArray(value) && !nodeUtilTypes.isProxy(value); } function optionalString(value: unknown, field: string): string | undefined { @@ -52,11 +56,154 @@ function requireString(value: unknown, field: string): string { return result; } +export function parseHardenedBossClientKind(value: unknown, field: string): HardenedBossClientKind | undefined { + if (value === undefined || value === null) return undefined; + if (value === "boss_participant" || value === "boss_reviewer") return value; + throw new Error(`${field} must be boss_participant or boss_reviewer`); +} + +function sandboxType(value: unknown): string | undefined { + if (!isRecord(value)) return undefined; + return typeof value.type === "string" ? value.type : undefined; +} + +export function assertHardenedBossAgentConfig(agent: BridgeAgentConfig): void { + if (nodeUtilTypes.isProxy(agent)) throw new Error("Hardened Boss agent config must not be a proxy"); + assertBossCanonicalData(agent, "$.agent"); + if (agent.bossClient === undefined) return; + if (agent.sandboxPolicy !== undefined && !isRecord(agent.sandboxPolicy)) { + throw new Error(`${agent.bossClient} sandboxPolicy must be a plain object`); + } + if (agent.approvalPolicy !== undefined && typeof agent.approvalPolicy !== "string") { + throw new Error(`${agent.bossClient} approvalPolicy must be a string`); + } + const type = sandboxType(agent.sandboxPolicy); + if (type === "dangerFullAccess" || type === "danger-full-access") { + throw new Error(`${agent.bossClient} cannot use danger-full-access`); + } + if (agent.approvalPolicy === "never") { + throw new Error(`${agent.bossClient} cannot disable approval checks`); + } + if (agent.bossClient === "boss_reviewer" && type !== undefined && type !== "readOnly" && type !== "read-only") { + throw new Error("boss_reviewer must use a read-only sandbox"); + } + const canonicalCwd = resolve(agent.cwd); + if (agent.bossClient === "boss_participant" && canonicalCwd === parsePath(canonicalCwd).root) { + throw new Error("boss_participant workspace root must not be a filesystem root"); + } + if (isRecord(agent.sandboxPolicy)) { + assertBossCanonicalData(agent.sandboxPolicy, "$.agent.sandboxPolicy"); + const allowedKeys = type === "workspaceWrite" || type === "workspace-write" + ? new Set(["type", "writableRoots", "networkAccess"]) + : new Set(["type", "networkAccess"]); + const keys = Reflect.ownKeys(agent.sandboxPolicy); + if (keys.some((key) => typeof key !== "string" || !allowedKeys.has(key))) { + throw new Error(`${agent.bossClient} sandboxPolicy contains unsupported capability fields`); + } + if (agent.sandboxPolicy.networkAccess !== false) { + throw new Error(`${agent.bossClient} networkAccess must be false`); + } + if (type !== "readOnly" && type !== "read-only" && type !== "workspaceWrite" && type !== "workspace-write") { + throw new Error(`${agent.bossClient} sandboxPolicy type is unsupported`); + } + } + if (isRecord(agent.sandboxPolicy) && (type === "workspaceWrite" || type === "workspace-write")) { + const roots = agent.sandboxPolicy.writableRoots; + assertBossCanonicalData(roots, "$.agent.sandboxPolicy.writableRoots"); + if (!Array.isArray(roots) || nodeUtilTypes.isProxy(roots) || roots.some((root) => typeof root !== "string")) { + throw new Error(`${agent.bossClient} writableRoots must be a dense string array`); + } + if ( + agent.bossClient === "boss_reviewer" + || roots.length !== 1 + || resolve(roots[0]) !== canonicalCwd + ) { + throw new Error(`${agent.bossClient} writable roots must be restricted to the agent cwd`); + } + } + if (agent.bossClient === "boss_participant") { + // This adapter has no protected broker/assignment projection carrying a + // canonical workspace root. Caller cwd/config/env values cannot supply + // that authority, so writable protected launches remain dormant. + throw new Error("boss_participant requires unavailable broker-owned assigned workspace authority"); + } +} + +export function bridgeAgentApprovalPolicy(agent: BridgeAgentConfig): unknown { + return agent.approvalPolicy + ?? (agent.bossClient === undefined ? "never" : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].approvalPolicy); +} + +export function bridgeAgentDefaultSandbox(agent: BridgeAgentConfig): "read-only" | "workspace-write" | undefined { + return agent.bossClient === undefined ? undefined : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].sandbox; +} + +export function assertHardenedBossBridgeConfig(config: BridgeConfig): void { + assertBossCanonicalData(config, "$.bridgeConfig"); + if (nodeUtilTypes.isProxy(config) || nodeUtilTypes.isProxy(config.agents) || !Array.isArray(config.agents)) { + throw new Error("Bridge config and agents must be plain non-proxy data"); + } + for (let index = 0; index < config.agents.length; index += 1) { + if (!Object.hasOwn(config.agents, index)) throw new Error("Bridge agents must not be sparse"); + const agent = config.agents[index]; + if (typeof agent !== "object" || agent === null || Array.isArray(agent) || nodeUtilTypes.isProxy(agent)) { + throw new Error("Bridge agents must be plain non-proxy objects"); + } + } + if (!config.agents.some((agent) => agent.bossClient !== undefined)) return; + if (config.appServer !== undefined) { + assertBossCanonicalData(config.appServer, "$.appServer"); + if (nodeUtilTypes.isProxy(config.appServer)) throw new Error("Hardened Boss app-server config must not be a proxy"); + if (config.appServer.command !== undefined || config.appServer.startDaemonCommand !== undefined) { + throw new Error("Hardened Boss bridge cannot use caller-provided app-server commands"); + } + } + for (const args of [config.appServer?.args, config.appServer?.startDaemonArgs]) { + if (!args) continue; + assertBossCanonicalData(args, "$.appServer.argv"); + if (nodeUtilTypes.isProxy(args) || args.some((arg) => typeof arg !== "string")) throw new Error("Hardened Boss bridge arguments must be dense string arrays"); + for (const arg of args) { + if (arg === "--") { + continue; + } + if (arg.length > 2 && arg.startsWith("-C")) { + throw new Error("Hardened Boss bridge cannot pass launch escape -C to app-server"); + } + const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; + if ( + ["-c", "--config", "-p", "--profile", "--enable", "--disable"].includes(optionName) + || (optionName.startsWith("-c") && optionName !== "-C") + || optionName.startsWith("-p") + ) { + throw new Error(`Hardened Boss bridge cannot pass raw ${optionName} or profile configuration to app-server`); + } + if (["--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-hook-trust", "--yolo", "--add-dir", "--cd", "-C"].includes(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + if ( + ["--sandbox", "--ask-for-approval"].includes(optionName) + || optionName === "-s" + || optionName === "-a" + || optionName.startsWith("-s") + || optionName.startsWith("-a") + ) { + throw new Error(`Hardened Boss bridge cannot pass policy override ${optionName} to app-server`); + } + if (optionName.startsWith("-") && /(?:yolo|danger|bypass)/i.test(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + } + } + // The aggregate validator is itself a complete pre-spawn boundary; callers + // cannot validate argv while bypassing per-agent authority ceilings. + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); +} + function normalizeAgent(raw: unknown, index: number): BridgeAgentConfig { if (!isRecord(raw)) throw new Error(`agents[${index}] must be an object`); const id = requireString(raw.id, `agents[${index}].id`); const name = optionalString(raw.name, `agents[${index}].name`) ?? id; - return { + const agent: BridgeAgentConfig = { id, name, cwd: resolve(optionalString(raw.cwd, `agents[${index}].cwd`) ?? processCwd()), @@ -65,20 +212,27 @@ function normalizeAgent(raw: unknown, index: number): BridgeAgentConfig { instructions: optionalString(raw.instructions, `agents[${index}].instructions`), approvalPolicy: raw.approvalPolicy, sandboxPolicy: raw.sandboxPolicy, + bossClient: parseHardenedBossClientKind(raw.bossClient, `agents[${index}].bossClient`), }; + assertHardenedBossAgentConfig(agent); + return agent; } export function defaultBridgeConfig(env: NodeJS.ProcessEnv = process.env): BridgeConfig { const id = env.CODEX_INTERCOM_BRIDGE_ID?.trim() || "codex-worker"; + const bossClient = parseHardenedBossClientKind(env.CODEX_INTERCOM_BOSS_CLIENT?.trim(), "CODEX_INTERCOM_BOSS_CLIENT"); + const agent: BridgeAgentConfig = { + id, + name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, + cwd: resolve(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), + model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || undefined, + instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || undefined, + ...(bossClient === undefined ? {} : { bossClient }), + }; + assertHardenedBossAgentConfig(agent); return { statePath: env.CODEX_INTERCOM_BRIDGE_STATE?.trim() || DEFAULT_BRIDGE_STATE_PATH, - agents: [{ - id, - name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, - cwd: resolve(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), - model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || undefined, - instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || undefined, - }], + agents: [agent], }; } diff --git a/codex/bridge-daemon.test.ts b/codex/bridge-daemon.test.ts index c3160f4..f5227c8 100644 --- a/codex/bridge-daemon.test.ts +++ b/codex/bridge-daemon.test.ts @@ -1,8 +1,11 @@ import test from "node:test"; import assert from "node:assert/strict"; import { EventEmitter } from "node:events"; +import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import type { IntercomClient } from "../broker/client.ts"; -import { VirtualCodexAgent, getApprovedIntercomSend, getApprovedIntercomToolFromApproval, getCompletedIntercomSend, isIntercomToolApprovalRequest, threadSandboxMode } from "./bridge-daemon.ts"; +import { CodexBridgeDaemon, VirtualCodexAgent, getApprovedIntercomSend, getApprovedIntercomToolFromApproval, getCompletedIntercomSend, isIntercomToolApprovalRequest, threadSandboxMode } from "./bridge-daemon.ts"; class FakeIntercomClient extends EventEmitter { connected = false; @@ -155,3 +158,28 @@ test("threadSandboxMode maps bridge sandbox policies to codex thread modes", () assert.equal(threadSandboxMode({ type: "dangerFullAccess" }), "danger-full-access"); assert.equal(threadSandboxMode(undefined), "read-only"); }); + +test("protected bridge rejects hostile PATH Codex before app-client or process creation", () => { + const dir = mkdtempSync(join(tmpdir(), "codex-provider-bridge-")); + const marker = join(dir, "executed"); + const executable = join(dir, "codex"); + writeFileSync(executable, `#!/bin/sh\nprintf hostile > '${marker}'\n`); + chmodSync(executable, 0o755); + const previousPath = process.env.PATH; + process.env.PATH = dir; + try { + assert.throws( + () => new CodexBridgeDaemon({ + statePath: join(dir, "state.json"), + agents: [{ id: "reviewer", name: "reviewer", cwd: dir, bossClient: "boss_reviewer" }], + }), + (error: unknown) => (error as { code?: unknown }).code === "PROVIDER_AUTHORITY_UNAVAILABLE", + ); + assert.equal(existsSync(marker), false); + assert.equal(existsSync(join(dir, "state.json")), false); + } finally { + if (previousPath === undefined) delete process.env.PATH; + else process.env.PATH = previousPath; + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/codex/bridge-daemon.ts b/codex/bridge-daemon.ts index 62415f6..4342d02 100644 --- a/codex/bridge-daemon.ts +++ b/codex/bridge-daemon.ts @@ -2,10 +2,23 @@ import { once } from "node:events"; import { randomUUID } from "node:crypto"; import { basename } from "node:path"; import { CodexAppServerClient, defaultServerRequestResponse, type JsonRpcMessage } from "./app-server-client.ts"; -import { loadBridgeConfig, loadBridgeState, saveBridgeState, type BridgeAgentConfig, type BridgeConfig, type BridgeState } from "./bridge-config.ts"; +import { + bridgeAgentApprovalPolicy, + bridgeAgentDefaultSandbox, + assertHardenedBossAgentConfig, + assertHardenedBossBridgeConfig, + loadBridgeConfig, + loadBridgeState, + saveBridgeState, + type BridgeAgentConfig, + type BridgeConfig, + type BridgeState, +} from "./bridge-config.ts"; import { IntercomClient } from "../broker/client.ts"; +import { assertBossCanonicalData } from "../broker/boss-adapter.ts"; import { spawnBrokerIfNeeded } from "../broker/spawn.ts"; import { DEFAULT_ASK_TIMEOUT_MS, loadConfig, validateAskTimeoutMs } from "../config.ts"; +import { assertHardenedBossProviderAuthority, type HardenedBossClientKind } from "./boss-client.ts"; import type { Message, SessionInfo } from "../types.ts"; import { resolveContactTarget, type IntercomContact } from "./contact.ts"; import { formatAttachments, formatSessionDisplay, formatSessionList, resolveSessionTarget, type ToolResult } from "./runtime.ts"; @@ -120,6 +133,31 @@ export function threadSandboxMode(sandboxPolicy: unknown): string { } } +function protectedBossClientForBridge(config: BridgeConfig): HardenedBossClientKind | undefined { + // Reject accessors/proxies before reading a deny-only launch marker. + assertBossCanonicalData(config, "$.bridgeConfig"); + if (!Array.isArray(config.agents)) return undefined; + for (const agent of config.agents) { + if (typeof agent !== "object" || agent === null || Array.isArray(agent)) continue; + if (agent.bossClient === "boss_participant" || agent.bossClient === "boss_reviewer") return agent.bossClient; + } + return undefined; +} + +function bridgeAgentSandboxMode(agent: BridgeAgentConfig): string { + return agent.sandboxPolicy === undefined + ? bridgeAgentDefaultSandbox(agent) ?? "read-only" + : threadSandboxMode(agent.sandboxPolicy); +} + +function bridgeAgentTurnSandboxPolicy(agent: BridgeAgentConfig): unknown { + if (agent.sandboxPolicy !== undefined) return agent.sandboxPolicy; + if (bridgeAgentSandboxMode(agent) === "workspace-write") { + throw new Error("workspace-write requires unavailable broker-owned assigned workspace authority"); + } + return { type: "readOnly", networkAccess: false }; +} + function getTurnId(result: unknown): string { const turn = result && typeof result === "object" ? (result as Record).turn : undefined; if (!turn || typeof turn !== "object" || typeof (turn as Record).id !== "string") { @@ -435,12 +473,12 @@ export class VirtualCodexAgent { async ensureThread(): Promise { if (this.threadId) { try { - const sandbox = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox = bridgeAgentSandboxMode(this.agent); await this.app.request("thread/resume", { threadId: this.threadId, cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox, }); return this.threadId; @@ -449,11 +487,11 @@ export class VirtualCodexAgent { } } - const sandbox = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox = bridgeAgentSandboxMode(this.agent); const result = await this.app.request("thread/start", { cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox, serviceName: "codex-intercom", developerInstructions: this.agent.instructions ?? null, @@ -511,8 +549,8 @@ export class VirtualCodexAgent { threadId, input, cwd: this.agent.cwd, - approvalPolicy: this.agent.approvalPolicy ?? "never", - sandboxPolicy: this.agent.sandboxPolicy ?? { type: "readOnly", networkAccess: false }, + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), + sandboxPolicy: bridgeAgentTurnSandboxPolicy(this.agent), model: this.agent.model ?? null, }); } @@ -738,11 +776,22 @@ export class CodexBridgeDaemon { private inflightToolCalls = new Map(); constructor(private readonly config: BridgeConfig, private readonly hooks: CodexBridgeHooks = {}) { - this.app = new CodexAppServerClient(config.appServer); + // A marker is deny-only here: no config field can provide executable or + // artifact authority. + const protectedBossClient = protectedBossClientForBridge(config); + assertHardenedBossProviderAuthority(protectedBossClient); + assertHardenedBossBridgeConfig(config); + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); + this.app = new CodexAppServerClient(config.appServer, protectedBossClient); this.app.setServerRequestHandler((message) => this.handleServerRequest(message)); } async start(): Promise { + // Revalidate immediately before CodexAppServerClient reaches either its + // daemon spawnSync or stdio spawn boundary. + assertHardenedBossProviderAuthority(protectedBossClientForBridge(this.config)); + assertHardenedBossBridgeConfig(this.config); + for (const agent of this.config.agents) assertHardenedBossAgentConfig(agent); const intercomConfig = loadConfig(); await spawnBrokerIfNeeded(intercomConfig.brokerCommand, intercomConfig.brokerArgs); await this.app.connect(); diff --git a/codex/coi.test.ts b/codex/coi.test.ts index 6606915..8d08667 100644 --- a/codex/coi.test.ts +++ b/codex/coi.test.ts @@ -1,9 +1,9 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { existsSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { buildCodexAppServerArgs, buildCoiTuiArgs, cleanupOldCoiStateFiles, createDefaultIdentity, deriveBridgeAgentRuntimeConfig, hasCodexHelpOrVersion, parseCoiArgs, resetCoiStateForFreshStart, resolveCoiResumeRequest, sanitizeSegment, splitCodexResumeArgs } from "./coi.ts"; +import { assertHardenedBossCoiLaunch, buildCodexAppServerArgs, buildCoiTuiArgs, cleanupOldCoiStateFiles, createDefaultIdentity, deriveBridgeAgentRuntimeConfig, hasCodexHelpOrVersion, parseCoiArgs, resetCoiStateForFreshStart, resolveCoiResumeRequest, runCoi, runInteractiveTui, sanitizeSegment, splitCodexResumeArgs } from "./coi.ts"; import { filterAltIInput, TuiInputDecoder } from "./tui-input.ts"; test("sanitizeSegment keeps readable safe ids", () => { @@ -46,7 +46,7 @@ test("parseCoiArgs leaves everything after separator for codex", () => { ], {}); assert.equal(parsed.name, "sidecar"); - assert.deepEqual(parsed.codexArgs, ["--name", "not-sidecar"]); + assert.deepEqual(parsed.codexArgs, ["--", "--name", "not-sidecar"]); }); test("parseCoiArgs supports disabling the Alt+I terminal shortcut", () => { @@ -59,6 +59,7 @@ test("splitCodexResumeArgs keeps options before the resumed thread id", () => { assert.deepEqual(splitCodexResumeArgs(["--profile", "cliproxy", "-m", "gpt-test", "hello there"]), { optionArgs: ["--profile", "cliproxy", "-m", "gpt-test"], promptArgs: ["hello there"], + separatorPresent: false, }); }); @@ -66,6 +67,14 @@ test("splitCodexResumeArgs respects explicit separator", () => { assert.deepEqual(splitCodexResumeArgs(["--no-alt-screen", "--", "--literal-prompt"]), { optionArgs: ["--no-alt-screen"], promptArgs: ["--literal-prompt"], + separatorPresent: true, + }); +}); + +test("explicit separator prevents a literal resume prompt from becoming a TUI subcommand", () => { + assert.deepEqual(resolveCoiResumeRequest(["--", "resume", "attacker-thread"]), { + optionArgs: [], + promptArgs: ["resume", "attacker-thread"], }); }); @@ -82,10 +91,18 @@ test("buildCoiTuiArgs opens a fresh remote TUI without resuming an empty sidecar "--remote", "unix:///tmp/coi.sock", "--profile", "work", ]); assert.deepEqual(buildCoiTuiArgs("unix:///tmp/coi.sock", [], "thread-requested", ["continue"], true), [ - "resume", "--remote", "unix:///tmp/coi.sock", "thread-requested", "continue", + "resume", "--remote", "unix:///tmp/coi.sock", "thread-requested", "--", "continue", ]); }); +test("TUI reconstruction preserves separator so prompt-shaped options cannot be promoted", () => { + assert.deepEqual(buildCoiTuiArgs("unix:///tmp/coi.sock", ["--no-alt-screen"], "thread-1", ["--literal-prompt"], false), [ + "--remote", "unix:///tmp/coi.sock", "--no-alt-screen", "--", "--literal-prompt", + ]); + assert.equal(hasCodexHelpOrVersion(["--", "--help"]), false); + assert.deepEqual(deriveBridgeAgentRuntimeConfig(["--", "--yolo"], "/tmp/project"), {}); +}); + test("buildCodexAppServerArgs forwards only app-server-compatible config options", () => { assert.deepEqual( buildCodexAppServerArgs([ @@ -123,6 +140,29 @@ test("buildCodexAppServerArgs forwards managed worker identity to the MCP subpro ); }); +test("buildCodexAppServerArgs forwards canonical Boss identity without deriving it from legacy runId", () => { + assert.deepEqual( + buildCodexAppServerArgs([], "/tmp/coi.sock", { + AGENT_INTERCOM_WORKER_ID: "worker-1", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-1", + AGENT_INTERCOM_WORKER_GENERATION: "2", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-1", + AGENT_INTERCOM_BINDING_EPOCH: "3", + }), + [ + "app-server", + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_WORKER_ID="worker-1"', + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_WORKER_INCARNATION_ID="incarnation-1"', + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_WORKER_GENERATION="2"', + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_BOSS_RUN_ID="boss-run-1"', + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_PARTICIPANT_ID="participant-1"', + "-c", 'mcp_servers.codex-intercom.env.AGENT_INTERCOM_BINDING_EPOCH="3"', + "--listen", "unix:///tmp/coi.sock", + ], + ); +}); + test("filterAltIInput removes legacy and enhanced Alt+I press encodings", () => { assert.deepEqual(filterAltIInput("before\x1biafter\x1b[105;3:1u!"), { forwarded: "beforeafter!", @@ -235,6 +275,10 @@ test("deriveBridgeAgentRuntimeConfig carries workspace-write sandbox roots", () }); }); +test("deriveBridgeAgentRuntimeConfig preserves omitted safe defaults", () => { + assert.deepEqual(deriveBridgeAgentRuntimeConfig([], "/tmp/project"), {}); +}); + test("deriveBridgeAgentRuntimeConfig supports short and bypass flags", () => { assert.deepEqual(deriveBridgeAgentRuntimeConfig(["-s=read-only", "-a", "never"], "/tmp/project"), { approvalPolicy: "never", @@ -247,6 +291,144 @@ test("deriveBridgeAgentRuntimeConfig supports short and bypass flags", () => { }); }); +test("hardened Boss launch rejects raw config, profiles, bypass aliases, and root expansion", () => { + for (const args of [ + ["-c", "sandbox_mode=\"danger-full-access\""], + ["--config=sandbox_mode=\"danger-full-access\""], + ["-csandbox_mode=\"danger-full-access\""], + ["-p", "unsafe-profile"], + ["-punsafe-profile"], + ["--profile=unsafe-profile"], + ["--enable", "untrusted-feature"], + ["--disable=approval_checks"], + ["--dangerously-bypass-approvals-and-sandbox"], + ["--dangerously-bypass-hook-trust"], + ["--yolo"], + ["--add-dir", "/tmp/outside"], + ["-C", "/tmp/outside"], + ]) { + assert.throws(() => assertHardenedBossCoiLaunch(args, "/tmp/project", "boss_participant")); + } + for (const override of ["--yolo", "--config=unsafe", "--profile=unsafe", "--enable=escape", "--add-dir=/tmp/outside", "--sandbox=danger-full-access", "--ask-for-approval=never"]) { + assert.throws(() => assertHardenedBossCoiLaunch(["--", override], "/tmp/project", "boss_participant")); + } +}); + +test("hardened Boss launch rejects attached short authority options before TUI and refresh spawn", () => { + for (const [arg, expected] of [ + ["-sworkspace-write", /attached -s policy override/], + ["-anever", /attached -a policy override/], + ["-C\/etc", /writable root/], + ] as const) { + for (const argv of [[arg], ["--", arg]]) { + assert.throws( + () => assertHardenedBossCoiLaunch(argv, "/tmp/project", "boss_reviewer"), + expected, + `must reject ${arg} in ${argv.join(" ")}`, + ); + } + } + + assert.deepEqual(assertHardenedBossCoiLaunch([], "/tmp/project", "boss_reviewer"), {}); + assert.deepEqual( + assertHardenedBossCoiLaunch(["-s", "read-only", "-a", "untrusted"], "/tmp/project", "boss_reviewer"), + { approvalPolicy: "untrusted", sandboxPolicy: { type: "readOnly", networkAccess: false } }, + ); + assert.deepEqual(assertHardenedBossCoiLaunch(["-sworkspace-write", "-anever", "-C/etc"], "/tmp/project", undefined), {}); +}); + +test("hardened Boss validation is proxy-first and zero-trap for raw argv", () => { + let trapCount = 0; + const proxy = new Proxy([], { + get() { trapCount += 1; throw new Error("trap"); }, + ownKeys() { trapCount += 1; throw new Error("trap"); }, + getOwnPropertyDescriptor() { trapCount += 1; throw new Error("trap"); }, + getPrototypeOf() { trapCount += 1; throw new Error("trap"); }, + }); + assert.throws(() => assertHardenedBossCoiLaunch(proxy, "/tmp/project", "boss_participant"), /proxies are not supported/); + assert.equal(trapCount, 0); +}); + +test("Boss production launch fails provider authority before help, args, or caller command inspection", async () => { + const base = { + cwd: "/tmp/project", + noTui: true, + copyShortcut: false, + codexCommand: "codex", + codexArgs: ["--help", "--yolo"], + }; + for (const options of [base, { ...base, codexCommand: "/attacker/codex", codexArgs: ["--help"] }]) { + await assert.rejects( + runCoi(options, { CODEX_INTERCOM_BOSS_CLIENT: "boss_participant" }), + (error: unknown) => (error as { code?: unknown }).code === "PROVIDER_AUTHORITY_UNAVAILABLE", + ); + } +}); + +test("hostile PATH Codex is never executed by protected coi headless, help, TUI, or refresh", async () => { + const dir = mkdtempSync(join(tmpdir(), "codex-provider-coi-")); + const marker = join(dir, "executed"); + const executable = join(dir, "codex"); + writeFileSync(executable, `#!/bin/sh\nprintf hostile > '${marker}'\n`); + chmodSync(executable, 0o755); + const hostileEnv = { ...process.env, PATH: dir, CODEX_INTERCOM_BOSS_CLIENT: "boss_reviewer" }; + const failsUnavailable = (error: unknown) => (error as { code?: unknown }).code === "PROVIDER_AUTHORITY_UNAVAILABLE"; + try { + const base = { cwd: dir, copyShortcut: false, codexCommand: "codex" }; + await assert.rejects(runCoi({ ...base, noTui: true, codexArgs: [] }, hostileEnv), failsUnavailable); + await assert.rejects(runCoi({ ...base, noTui: false, codexArgs: ["--help"] }, hostileEnv), failsUnavailable); + await assert.rejects(runCoi({ ...base, noTui: false, codexArgs: [] }, hostileEnv), failsUnavailable); + await assert.rejects( + runInteractiveTui("codex", ["initial"], ["resume", "thread-1"], dir, undefined, undefined, undefined, "boss_reviewer", hostileEnv), + failsUnavailable, + ); + assert.equal(existsSync(marker), false); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("ordinary coi and TUI keep explicit executable launch behavior", async () => { + const dir = mkdtempSync(join(tmpdir(), "codex-provider-ordinary-tui-")); + const marker = join(dir, "executed"); + const executable = join(dir, "explicit-codex"); + writeFileSync(executable, `#!/bin/sh\nprintf ordinary > '${marker}'\n`); + chmodSync(executable, 0o755); + try { + assert.equal(await runCoi({ + cwd: dir, + noTui: false, + copyShortcut: false, + codexCommand: executable, + codexArgs: ["--help"], + }, { ...process.env, PATH: dir }), 0); + assert.equal(existsSync(marker), true); + rmSync(marker); + assert.equal(await runInteractiveTui(executable, [], [], dir), 0); + assert.equal(existsSync(marker), true); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("hardened Boss launch preserves read-only reviewers and keeps writable participants dormant without root authority", () => { + assert.throws( + () => assertHardenedBossCoiLaunch(["--sandbox=workspace-write", "--ask-for-approval=untrusted"], "/tmp/project", "boss_participant"), + /broker-owned assigned workspace authority/, + ); + assert.deepEqual( + assertHardenedBossCoiLaunch(["--sandbox=read-only", "--ask-for-approval=untrusted"], "/tmp/project", "boss_reviewer"), + { + approvalPolicy: "untrusted", + sandboxPolicy: { type: "readOnly", networkAccess: false }, + }, + ); + assert.throws( + () => assertHardenedBossCoiLaunch(["--sandbox=workspace-write"], "/tmp/project", "boss_reviewer"), + /read-only/, + ); +}); + test("fresh worker startup removes the persisted Codex bridge thread state", () => { const dir = mkdtempSync(join(tmpdir(), "coi-fresh-start-")); try { diff --git a/codex/coi.ts b/codex/coi.ts index 62fcfb5..ab6468a 100644 --- a/codex/coi.ts +++ b/codex/coi.ts @@ -4,8 +4,16 @@ import { createHash } from "node:crypto"; import { existsSync, readdirSync, rmSync, statSync } from "node:fs"; import { basename, join, resolve } from "node:path"; import { setTimeout as delay } from "node:timers/promises"; +import { types as nodeUtilTypes } from "node:util"; import { CodexBridgeDaemon } from "./bridge-daemon.ts"; -import type { BridgeConfig } from "./bridge-config.ts"; +import { + assertHardenedBossAgentConfig, + parseHardenedBossClientKind, + type BridgeAgentConfig, + type BridgeConfig, +} from "./bridge-config.ts"; +import { assertHardenedBossProviderAuthority, type HardenedBossClientKind } from "./boss-client.ts"; +import { assertBossCanonicalData } from "../broker/boss-adapter.ts"; import { ensureIntercomRuntimeDir, getIntercomDirPath } from "../broker/paths.ts"; import { copyTextToClipboard, copyTextToTerminalClipboard } from "./clipboard.ts"; import { formatContactInstruction } from "./contact.ts"; @@ -60,7 +68,12 @@ const CODEX_OPTIONS_WITH_VALUE = new Set([ const COI_STATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; const MANAGED_MCP_ENV_KEYS = [ "AGENT_INTERCOM_WORKER_ID", + "AGENT_INTERCOM_WORKER_INCARNATION_ID", + "AGENT_INTERCOM_WORKER_GENERATION", "AGENT_INTERCOM_RUN_ID", + "AGENT_INTERCOM_BOSS_RUN_ID", + "AGENT_INTERCOM_PARTICIPANT_ID", + "AGENT_INTERCOM_BINDING_EPOCH", "AGENT_INTERCOM_MANAGER_TARGET", "AGENT_INTERCOM_MANAGER_SESSION_ID", "AGENT_INTERCOM_SYSTEMD_UNIT", @@ -152,27 +165,28 @@ export function deriveBridgeAgentRuntimeConfig(args: string[], cwd: string): Bri let sandboxMode: string | undefined; const writableRoots = new Set([resolve(cwd)]); - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; + const { optionArgs } = splitCodexResumeArgs(args); + for (let index = 0; index < optionArgs.length; index += 1) { + const arg = optionArgs[index]; const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; switch (optionName) { case "--ask-for-approval": case "-a": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); approvalPolicy = parsed.value; index = parsed.nextIndex; break; } case "--sandbox": case "-s": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); sandboxMode = parsed.value; index = parsed.nextIndex; break; } case "--add-dir": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); writableRoots.add(resolve(cwd, parsed.value)); index = parsed.nextIndex; break; @@ -200,6 +214,83 @@ export function deriveBridgeAgentRuntimeConfig(args: string[], cwd: string): Bri }; } +const HARDENED_BOSS_RAW_CONFIG_OPTIONS = new Set(["-c", "--config", "-p", "--profile", "--enable", "--disable"]); +const HARDENED_BOSS_BYPASS_OPTIONS = new Set([ + "--dangerously-bypass-approvals-and-sandbox", + "--dangerously-bypass-hook-trust", + "--yolo", +]); + +function isHardenedBossRawConfigOption(optionName: string): boolean { + return HARDENED_BOSS_RAW_CONFIG_OPTIONS.has(optionName) + || (optionName.startsWith("-c") && optionName !== "-C") + || optionName.startsWith("-p"); +} + +function hardenedBossAttachedAuthorityOption(arg: string): "-s" | "-a" | "-C" | undefined { + if (arg.length <= 2) return undefined; + const option = arg.slice(0, 2); + return option === "-s" || option === "-a" || option === "-C" ? option : undefined; +} + +/** Validate every user-controlled launch escape before an app-server exists. */ +export function assertHardenedBossCoiLaunch( + args: string[], + cwd: string, + bossClient: HardenedBossClientKind | undefined, +): BridgeRuntimeConfig { + if (bossClient !== undefined) { + assertBossCanonicalData(args, "$.argv"); + if (!Array.isArray(args) || nodeUtilTypes.isProxy(args) || args.some((arg) => typeof arg !== "string")) { + throw new Error(`${bossClient} arguments must be a plain dense string array`); + } + } + if (bossClient === undefined) return deriveBridgeAgentRuntimeConfig(args, cwd); + let afterSeparator = false; + for (const arg of args) { + if (arg === "--") { + afterSeparator = true; + continue; + } + const attachedAuthorityOption = hardenedBossAttachedAuthorityOption(arg); + if (attachedAuthorityOption === "-C") { + throw new Error(`${bossClient} cannot expand or replace its writable root`); + } + if (attachedAuthorityOption !== undefined) { + throw new Error(`${bossClient} cannot use attached ${attachedAuthorityOption} policy overrides`); + } + const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; + if (isHardenedBossRawConfigOption(optionName)) { + throw new Error(`${bossClient} cannot use raw ${optionName} or profile configuration`); + } + if (HARDENED_BOSS_BYPASS_OPTIONS.has(optionName)) { + throw new Error(`${bossClient} cannot use approval or sandbox bypass aliases`); + } + if (optionName === "--add-dir" || optionName === "--cd" || optionName === "-C") { + throw new Error(`${bossClient} cannot expand or replace its writable root`); + } + if (afterSeparator && ["--sandbox", "-s", "--ask-for-approval", "-a"].includes(optionName)) { + throw new Error(`${bossClient} cannot place policy overrides after the argument separator`); + } + if (optionName.startsWith("-") && /(?:yolo|danger|bypass)/i.test(optionName)) { + throw new Error(`${bossClient} cannot use approval or sandbox bypass aliases`); + } + } + if (bossClient === "boss_participant") { + throw new Error("boss_participant requires unavailable broker-owned assigned workspace authority"); + } + const runtime = deriveBridgeAgentRuntimeConfig(args, cwd); + const probe: BridgeAgentConfig = { + id: "launch-validation", + name: "launch-validation", + cwd: resolve(cwd), + bossClient, + ...runtime, + }; + assertHardenedBossAgentConfig(probe); + return runtime; +} + export function parseCoiArgs(argv: string[], env: NodeJS.ProcessEnv = process.env): CoiOptions { const codexArgs: string[] = []; const options: Partial = {}; @@ -214,6 +305,7 @@ export function parseCoiArgs(argv: string[], env: NodeJS.ProcessEnv = process.en if (arg === "--") { afterSeparator = true; + codexArgs.push(arg); continue; } @@ -277,10 +369,10 @@ export function parseCoiArgs(argv: string[], env: NodeJS.ProcessEnv = process.en } export function hasCodexHelpOrVersion(args: string[]): boolean { - return args.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V"); + return splitCodexResumeArgs(args).optionArgs.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V"); } -export function splitCodexResumeArgs(args: string[]): { optionArgs: string[]; promptArgs: string[] } { +export function splitCodexResumeArgs(args: string[]): { optionArgs: string[]; promptArgs: string[]; separatorPresent: boolean } { const optionArgs: string[] = []; const promptArgs: string[] = []; let index = 0; @@ -288,7 +380,7 @@ export function splitCodexResumeArgs(args: string[]): { optionArgs: string[]; pr const arg = args[index]; if (arg === "--") { promptArgs.push(...args.slice(index + 1)); - return { optionArgs, promptArgs }; + return { optionArgs, promptArgs, separatorPresent: true }; } if (!arg.startsWith("-") || arg === "-") break; optionArgs.push(arg); @@ -299,7 +391,7 @@ export function splitCodexResumeArgs(args: string[]): { optionArgs: string[]; pr } } promptArgs.push(...args.slice(index)); - return { optionArgs, promptArgs }; + return { optionArgs, promptArgs, separatorPresent: false }; } export function resolveCoiResumeRequest(args: string[]): { @@ -307,8 +399,8 @@ export function resolveCoiResumeRequest(args: string[]): { promptArgs: string[]; threadId?: string; } { - const { optionArgs, promptArgs } = splitCodexResumeArgs(args); - if (promptArgs[0] !== "resume" || !promptArgs[1]) return { optionArgs, promptArgs }; + const { optionArgs, promptArgs, separatorPresent } = splitCodexResumeArgs(args); + if (separatorPresent || promptArgs[0] !== "resume" || !promptArgs[1]) return { optionArgs, promptArgs }; return { optionArgs, threadId: promptArgs[1], promptArgs: promptArgs.slice(2) }; } @@ -319,9 +411,10 @@ export function buildCoiTuiArgs( promptArgs: string[], explicitResume: boolean, ): string[] { + const promptTail = promptArgs.length === 0 ? [] : ["--", ...promptArgs]; return explicitResume - ? ["resume", "--remote", remote, ...optionArgs, threadId, ...promptArgs] - : ["--remote", remote, ...optionArgs, ...promptArgs]; + ? ["resume", "--remote", remote, ...optionArgs, threadId, ...promptTail] + : ["--remote", remote, ...optionArgs, ...promptTail]; } export function buildCodexAppServerArgs( @@ -390,7 +483,7 @@ function terminalNotification(message: string): void { else process.stderr.write(`${safe}\n`); } -async function runInteractiveTui( +export async function runInteractiveTui( command: string, args: string[], refreshArgs: string[], @@ -398,9 +491,14 @@ async function runInteractiveTui( onAltI?: (controls: { insertText(text: string): void }) => void, onAltM?: (controls: { insertText(text: string): void }) => void, installRefresh?: (refresh: () => void) => () => void, + protectedBossClient?: HardenedBossClientKind, + launchEnv: NodeJS.ProcessEnv = process.env, ): Promise { + // This also runs on the recursive refresh path below, so a future caller + // cannot reintroduce an ambient-PATH spawn after the initial preflight. + assertHardenedBossProviderAuthority(protectedBossClient); const runInherited = async (): Promise => { - const tui = spawn(command, args, { cwd, env: process.env, stdio: "inherit" }); + const tui = spawn(command, args, { cwd, env: launchEnv, stdio: "inherit" }); const [code, signal] = await once(tui, "exit") as [number | null, NodeJS.Signals | null]; if (typeof code === "number") return code; return signal === "SIGINT" ? 130 : 1; @@ -419,11 +517,11 @@ async function runInteractiveTui( } const tui = nodePty.spawn(command, args, { - name: process.env.TERM || "xterm-256color", + name: launchEnv.TERM || "xterm-256color", cols: process.stdout.columns || 80, rows: process.stdout.rows || 24, cwd, - env: process.env, + env: launchEnv, }); const outputSubscription = tui.onData((data) => process.stdout.write(data)); let refreshRequested = false; @@ -491,7 +589,7 @@ async function runInteractiveTui( outputSubscription.dispose(); } if (refreshRequested) { - return runInteractiveTui(command, refreshArgs, refreshArgs, cwd, onAltI, onAltM, installRefresh); + return runInteractiveTui(command, refreshArgs, refreshArgs, cwd, onAltI, onAltM, installRefresh, protectedBossClient, launchEnv); } return exitCode; } @@ -520,11 +618,19 @@ export function resetCoiStateForFreshStart(statePath: string, fresh: boolean): v if (fresh) rmSync(statePath, { force: true }); } -export async function runCoi(options: CoiOptions): Promise { +export async function runCoi(options: CoiOptions, env: NodeJS.ProcessEnv = process.env): Promise { + const bossClient = parseHardenedBossClientKind(env.CODEX_INTERCOM_BOSS_CLIENT?.trim(), "CODEX_INTERCOM_BOSS_CLIENT"); + // No provider executable/artifact authority is broker-owned yet. Fail + // before help, app-server, headless, TUI, refresh, or runtime-file setup. + assertHardenedBossProviderAuthority(bossClient); + if (bossClient !== undefined && options.codexCommand !== "codex") { + throw new Error(`${bossClient} cannot use a caller-provided Codex/app-server command`); + } + const runtimeConfig = assertHardenedBossCoiLaunch(options.codexArgs, options.cwd, bossClient); if (hasCodexHelpOrVersion(options.codexArgs)) { const help = spawn(options.codexCommand, options.codexArgs, { cwd: options.cwd, - env: process.env, + env, stdio: "inherit", }); const [code, signal] = await once(help, "exit") as [number | null, NodeJS.Signals | null]; @@ -540,17 +646,30 @@ export async function runCoi(options: CoiOptions): Promise { cleanupOldCoiStateFiles(intercomDir); const socketPath = options.socketPath ?? join(intercomDir, `coi-${process.pid}.sock`); const statePath = options.statePath ?? join(intercomDir, `coi-${sanitizeSegment(id)}-state.json`); - const fresh = process.env.AGENT_INTERCOM_FRESH === "1"; + const fresh = env.AGENT_INTERCOM_FRESH === "1"; resetCoiStateForFreshStart(statePath, fresh); rmSync(socketPath, { force: true }); - const appServer = spawn(options.codexCommand, buildCodexAppServerArgs(options.codexArgs, socketPath), { + const resumeRequest = resolveCoiResumeRequest(options.codexArgs); + const agent: BridgeAgentConfig = { + id, + name, + cwd: options.cwd, + model: env.CODEX_INTERCOM_MODEL, + instructions: options.instructions, + threadId: fresh ? undefined : resumeRequest.threadId, + ...(bossClient === undefined ? {} : { bossClient }), + ...runtimeConfig, + }; + assertHardenedBossAgentConfig(agent); + + const appServer = spawn(options.codexCommand, buildCodexAppServerArgs(options.codexArgs, socketPath, env), { cwd: options.cwd, - env: process.env, + env, stdio: ["ignore", "ignore", "pipe"], }); appServer.stderr?.on("data", (chunk) => { - if (process.env.CODEX_INTERCOM_DEBUG) process.stderr.write(String(chunk)); + if (env.CODEX_INTERCOM_DEBUG) process.stderr.write(String(chunk)); }); const cleanup = async () => { @@ -576,22 +695,13 @@ export async function runCoi(options: CoiOptions): Promise { await waitForSocket(socketPath, appServer); - const resumeRequest = resolveCoiResumeRequest(options.codexArgs); const config: BridgeConfig = { statePath, appServer: { transport: "unix-websocket", socketPath, }, - agents: [{ - id, - name, - cwd: options.cwd, - model: process.env.CODEX_INTERCOM_MODEL, - instructions: options.instructions, - threadId: fresh ? undefined : resumeRequest.threadId, - ...deriveBridgeAgentRuntimeConfig(options.codexArgs, options.cwd), - }], + agents: [agent], }; let refreshVisibleTui: (() => void) | undefined; daemon = new CodexBridgeDaemon(config, { @@ -628,7 +738,7 @@ export async function runCoi(options: CoiOptions): Promise { void daemon!.getContactTargetForAgent(id) .then(async (contact) => { const instruction = formatContactInstruction(contact); - const preferTerminal = Boolean(process.env.SSH_TTY || process.env.SSH_CONNECTION); + const preferTerminal = Boolean(env.SSH_TTY || env.SSH_CONNECTION); let copied = preferTerminal ? copyTextToTerminalClipboard(instruction, (sequence) => process.stdout.write(sequence)) : await copyTextToClipboard(instruction); @@ -668,6 +778,8 @@ export async function runCoi(options: CoiOptions): Promise { if (refreshVisibleTui === refresh) refreshVisibleTui = undefined; }; }, + bossClient, + env, ); } finally { await cleanupOnce(); diff --git a/codex/team.test.ts b/codex/team.test.ts index 7321e3e..a36dbf3 100644 --- a/codex/team.test.ts +++ b/codex/team.test.ts @@ -3,6 +3,309 @@ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; -import { formatIntercomTeam, resolveIntercomTeam } from "./team.ts"; -const worker = (id: string, runId: string, managerSessionId: string, state = "running") => ({ id, runId, harness: "codex", role: "reviewer", state, owned: true, managerSessionId, intercomTarget: id }); -test("team discovery follows the orchestrator owner instead of stale worker environment", async () => { const agentDir = await mkdtemp(join(tmpdir(), "codex-team-")); const dir = join(agentDir, "intercom", "orchestrator"); await mkdir(dir, { recursive: true }); try { await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 1, workers: [worker("self", "run-self", "manager-new"), worker("peer", "run-peer", "manager-new"), worker("old", "run-old", "manager-old")] })); const team = await resolveIntercomTeam({ selfId: "mcp-helper", agentDir, env: { AGENT_INTERCOM_WORKER_ID: "self", AGENT_INTERCOM_RUN_ID: "run-self", AGENT_INTERCOM_MANAGER_SESSION_ID: "manager-old" }, sessions: [{ id: "manager-new" }, { id: "peer" }] }); assert.equal(team.manager?.target, "manager-new"); assert.equal(team.manager?.connected, true); assert.deepEqual(team.coworkers.map((entry) => entry.id), ["peer"]); assert.match(formatIntercomTeam(team), /You: self/); } finally { await rm(agentDir, { recursive: true, force: true }); } }); +import { formatIntercomTeam, resolveIntercomTeam, type TeamSession } from "./team.ts"; + +const legacyWorker = (id: string, runId: string, managerSessionId: string, state = "running") => ({ + id, runId, harness: "codex", role: "reviewer", state, owned: true, managerSessionId, intercomTarget: id, +}); + +const bossWorker = ( + id: string, + bossRunId: string, + participantId: string, + role: "manager" | "worker" = "worker", + intercomTarget = id, +) => ({ + id, + workerIncarnationId: `incarnation-${id}`, + workerGeneration: 1, + bossRunId, + participantId, + bindingEpoch: 1, + harness: "codex", + role, + state: "working", + owned: true, + managerSessionId: "manager-session", + intercomTarget, +}); + +function bossSession(worker: ReturnType): TeamSession { + return { + id: worker.intercomTarget, + boss: { + binding: { + bossRunId: worker.bossRunId, + participantId: worker.participantId, + bindingEpoch: worker.bindingEpoch, + role: worker.role, + sessionId: worker.intercomTarget, + state: "active", + }, + workerIdentity: { + version: "orc.worker-identity.v2", + workerId: worker.id, + workerIncarnationId: worker.workerIncarnationId, + workerGeneration: worker.workerGeneration, + bossRunId: worker.bossRunId, + participantId: worker.participantId, + bindingEpoch: worker.bindingEpoch, + }, + participantState: worker.state, + }, + }; +} + +const bossEnv = { + AGENT_INTERCOM_WORKER_ID: "self", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-self", + AGENT_INTERCOM_WORKER_GENERATION: "1", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-self", + AGENT_INTERCOM_BINDING_EPOCH: "1", +}; + +test("ordinary team discovery follows the orchestrator owner instead of stale worker environment", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-team-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + await writeFile(join(dir, "workers.json"), JSON.stringify({ + version: 1, + workers: [legacyWorker("self", "run-self", "manager-new"), legacyWorker("peer", "run-peer", "manager-new"), legacyWorker("old", "run-old", "manager-old")], + })); + const team = await resolveIntercomTeam({ + selfId: "mcp-helper", + agentDir, + env: { AGENT_INTERCOM_WORKER_ID: "self", AGENT_INTERCOM_RUN_ID: "run-self", AGENT_INTERCOM_MANAGER_SESSION_ID: "manager-old" }, + sessions: [{ id: "manager-new" }, { id: "peer" }], + }); + assert.equal(team.manager?.target, "manager-new"); + assert.equal(team.manager?.connected, true); + assert.deepEqual(team.coworkers.map((entry) => entry.id), ["peer"]); + assert.match(formatIntercomTeam(team), /You: self/); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("Boss roster intersects exact session/run/participant/epoch/role/incarnation/generation/state", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-team-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const self = bossWorker("self", "boss-run-1", "participant-self", "worker", "self-session"); + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const peer = bossWorker("same-run", "boss-run-1", "participant-peer"); + const hidden = bossWorker("hidden-same-run", "boss-run-1", "participant-hidden"); + const other = bossWorker("other-run", "boss-run-2", "participant-other"); + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [self, manager, peer, hidden, other] })); + const team = await resolveIntercomTeam({ + selfId: "self-session", + agentDir, + env: bossEnv, + sessions: [bossSession(self), bossSession(manager), bossSession(peer), bossSession(other)], + }); + assert.equal(team.self.isManager, false); + assert.equal(team.manager?.connected, true); + assert.deepEqual(team.coworkers, [], "a Worker must not discover a sibling Worker"); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("a current-run Manager retains exact live visibility of its owned Workers", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-manager-team-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const workerOne = bossWorker("worker-one", "boss-run-1", "participant-one"); + const workerTwo = bossWorker("worker-two", "boss-run-1", "participant-two"); + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [manager, workerOne, workerTwo] })); + const team = await resolveIntercomTeam({ + selfId: "manager-session", + agentDir, + env: { + AGENT_INTERCOM_WORKER_ID: "manager", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-manager", + AGENT_INTERCOM_WORKER_GENERATION: "1", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-manager", + AGENT_INTERCOM_BINDING_EPOCH: "1", + }, + sessions: [bossSession(manager), bossSession(workerOne), bossSession(workerTwo)], + }); + assert.deepEqual(team.coworkers.map((entry) => entry.id), ["worker-one", "worker-two"]); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("a Boss Manager cannot discover a roster through a substituted selfId", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-substituted-self-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const worker = bossWorker("worker", "boss-run-1", "participant-worker", "worker", "worker-session"); + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [manager, worker] })); + const team = await resolveIntercomTeam({ + selfId: "worker-session", + agentDir, + env: { + AGENT_INTERCOM_WORKER_ID: "manager", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-manager", + AGENT_INTERCOM_WORKER_GENERATION: "1", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-manager", + AGENT_INTERCOM_BINDING_EPOCH: "1", + }, + sessions: [bossSession(manager), bossSession(worker)], + }); + assert.equal(team.self.id, "worker-session"); + assert.equal(team.self.isManager, false); + assert.equal(team.manager, undefined); + assert.deepEqual(team.coworkers, []); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("duplicate Boss current worker or live-session IDs fail closed", async () => { + for (const duplicate of ["worker", "session"] as const) { + const agentDir = await mkdtemp(join(tmpdir(), `codex-boss-duplicate-${duplicate}-`)); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const worker = bossWorker("worker", "boss-run-1", "participant-worker"); + const staleDuplicate = { ...manager, workerGeneration: 2, intercomTarget: "stale-manager-session" }; + const workers = duplicate === "worker" ? [manager, staleDuplicate, worker] : [manager, worker]; + const sessions = duplicate === "session" ? [bossSession(manager), bossSession(manager), bossSession(worker)] : [bossSession(manager), bossSession(worker)]; + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers })); + const team = await resolveIntercomTeam({ + selfId: "manager-session", + agentDir, + env: { + AGENT_INTERCOM_WORKER_ID: "manager", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-manager", + AGENT_INTERCOM_WORKER_GENERATION: "1", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-manager", + AGENT_INTERCOM_BINDING_EPOCH: "1", + }, + sessions, + }); + assert.equal(team.self.isManager, false, `${duplicate} duplication must not confer Manager discovery`); + assert.equal(team.manager, undefined); + assert.deepEqual(team.coworkers, []); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } + } +}); + +test("a stale current Boss live binding cannot unlock Manager roster discovery", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-stale-binding-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const worker = bossWorker("worker", "boss-run-1", "participant-worker"); + const staleManagerSession = bossSession(manager); + staleManagerSession.boss!.binding!.bindingEpoch = 2; + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [manager, worker] })); + const team = await resolveIntercomTeam({ + selfId: "manager-session", + agentDir, + env: { + AGENT_INTERCOM_WORKER_ID: "manager", + AGENT_INTERCOM_WORKER_INCARNATION_ID: "incarnation-manager", + AGENT_INTERCOM_WORKER_GENERATION: "1", + AGENT_INTERCOM_BOSS_RUN_ID: "boss-run-1", + AGENT_INTERCOM_PARTICIPANT_ID: "participant-manager", + AGENT_INTERCOM_BINDING_EPOCH: "1", + }, + sessions: [staleManagerSession, bossSession(worker)], + }); + assert.equal(team.self.isManager, false); + assert.equal(team.manager, undefined); + assert.deepEqual(team.coworkers, []); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("a self-consistent foreign-run Manager is never projected as connected", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-foreign-manager-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const self = bossWorker("self", "boss-run-1", "participant-self", "worker", "self-session"); + const foreignManager = bossWorker("manager", "boss-run-2", "participant-manager", "manager", "manager-session"); + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [self, foreignManager] })); + const team = await resolveIntercomTeam({ + selfId: "self-session", + agentDir, + env: bossEnv, + sessions: [bossSession(self), bossSession(foreignManager)], + }); + assert.deepEqual(team.manager, { target: "manager-session", connected: false }); + assert.deepEqual(team.coworkers, []); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); + +test("every substituted Boss roster identity dimension fails closed", async () => { + const dimensions = [ + ["session", (session: TeamSession) => { session.id = "substituted-session"; session.name = "peer"; }], + ["binding session", (session: TeamSession) => { session.boss!.binding!.sessionId = "substituted-session"; }], + ["run", (session: TeamSession) => { session.boss!.binding!.bossRunId = "other-run"; }], + ["participant", (session: TeamSession) => { session.boss!.binding!.participantId = "other-participant"; }], + ["epoch", (session: TeamSession) => { session.boss!.binding!.bindingEpoch = 2; }], + ["role", (session: TeamSession) => { session.boss!.binding!.role = "scout"; }], + ["incarnation", (session: TeamSession) => { (session.boss!.workerIdentity as Record).workerIncarnationId = "other-incarnation"; }], + ["generation", (session: TeamSession) => { (session.boss!.workerIdentity as Record).workerGeneration = 2; }], + ["state", (session: TeamSession) => { session.boss!.participantState = "waiting"; }], + ] as const; + for (const [name, mutate] of dimensions) { + const agentDir = await mkdtemp(join(tmpdir(), `codex-boss-team-${name.replaceAll(" ", "-")}-`)); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const self = bossWorker("self", "boss-run-1", "participant-self", "worker", "self-session"); + const manager = bossWorker("manager", "boss-run-1", "participant-manager", "manager", "manager-session"); + const peer = bossWorker("peer", "boss-run-1", "participant-peer"); + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [self, manager, peer] })); + const peerSession = bossSession(peer); + mutate(peerSession); + const team = await resolveIntercomTeam({ + selfId: "self-session", + agentDir, + env: bossEnv, + sessions: [bossSession(self), bossSession(manager), peerSession], + }); + assert.deepEqual(team.coworkers, [], `${name} substitution must be hidden`); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } + } +}); + +test("stale current Boss identity is not promoted to Manager", async () => { + const agentDir = await mkdtemp(join(tmpdir(), "codex-boss-team-stale-")); + const dir = join(agentDir, "intercom", "orchestrator"); + await mkdir(dir, { recursive: true }); + try { + const self = { ...bossWorker("self", "boss-run-1", "participant-self", "worker", "self-session"), workerGeneration: 2 }; + await writeFile(join(dir, "workers.json"), JSON.stringify({ version: 2, workers: [self] })); + const team = await resolveIntercomTeam({ selfId: "self-session", agentDir, env: bossEnv, sessions: [bossSession(self)] }); + assert.equal(team.self.isManager, false); + assert.equal(team.manager, undefined); + assert.deepEqual(team.coworkers, []); + } finally { + await rm(agentDir, { recursive: true, force: true }); + } +}); diff --git a/codex/team.ts b/codex/team.ts index 7daed5f..e53ec32 100644 --- a/codex/team.ts +++ b/codex/team.ts @@ -1,46 +1,249 @@ import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { getAgentDirPath } from "../broker/paths.ts"; +import { + BOSS_PARTICIPANT_ROLES, + parseParticipantState, + parseWorkerIdentityV2, + workerIdentityFromEnvironment, + type WorkerIdentityV2, +} from "@dataforxyz/agent-intercom-core/boss"; + +export interface TeamSession { + id: string; + name?: string; + boss?: { + binding?: { + bossRunId?: unknown; + participantId?: unknown; + bindingEpoch?: unknown; + role?: unknown; + sessionId?: unknown; + state?: unknown; + }; + workerIdentity?: unknown; + participantState?: unknown; + }; +} + +interface StoredWorker { + id?: unknown; + runId?: unknown; + workerIncarnationId?: unknown; + workerGeneration?: unknown; + bossRunId?: unknown; + participantId?: unknown; + bindingEpoch?: unknown; + harness?: unknown; + role?: unknown; + state?: unknown; + owned?: unknown; + managerSessionId?: unknown; + intercomTarget?: unknown; + canonicalIdentity?: WorkerIdentityV2; +} -export interface TeamSession { id: string; name?: string; } -interface StoredWorker { id?: unknown; runId?: unknown; harness?: unknown; role?: unknown; state?: unknown; owned?: unknown; managerSessionId?: unknown; intercomTarget?: unknown; } export interface TeamMember { id: string; target: string; harness?: string; role?: string; state?: string; connected: boolean; } export interface IntercomTeam { teamId?: string; self: { id: string; workerId?: string; isManager: boolean }; manager?: { target: string; connected: boolean }; coworkers: TeamMember[]; } -const LIVE_STATES = new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +const LEGACY_LIVE_STATES = new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +const CANONICAL_LIVE_STATES = new Set(["provisioning", "registering", "ready", "working", "waiting", "paused", "stalled", "blocked", "unreachable"]); const stringValue = (value: unknown): string | undefined => typeof value === "string" && value.trim() ? value.trim() : undefined; const connectedTo = (sessions: TeamSession[], target: string): boolean => { const normalized = target.toLowerCase(); return sessions.some((session) => session.id === target || session.name?.toLowerCase() === normalized); }; -async function readWorkers(agentDir: string): Promise { + +function bossIdentityFromEnvironment(env: NodeJS.ProcessEnv): WorkerIdentityV2 | undefined { + const bossKeys = ["AGENT_INTERCOM_BOSS_RUN_ID", "AGENT_INTERCOM_PARTICIPANT_ID", "AGENT_INTERCOM_BINDING_EPOCH"] as const; + if (!bossKeys.some((key) => env[key] !== undefined)) return undefined; + const identity = workerIdentityFromEnvironment(env); + if (!("bossRunId" in identity)) throw new Error("Incomplete Boss worker identity cannot discover a team"); + return identity; +} + +function canonicalWorker(value: unknown): StoredWorker { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("worker must be an object"); + const worker = value as StoredWorker; + const identity = parseWorkerIdentityV2({ + version: "orc.worker-identity.v2", + workerId: worker.id, + workerIncarnationId: worker.workerIncarnationId, + workerGeneration: worker.workerGeneration, + ...(worker.bossRunId === undefined ? {} : { bossRunId: worker.bossRunId }), + ...(worker.participantId === undefined ? {} : { participantId: worker.participantId }), + ...(worker.bindingEpoch === undefined ? {} : { bindingEpoch: worker.bindingEpoch }), + }); + parseParticipantState(worker.state, "$.worker.state"); + if (typeof worker.role !== "string" || !BOSS_PARTICIPANT_ROLES.includes(worker.role as never)) { + throw new Error("worker role is not canonical"); + } + if (worker.owned !== true || !stringValue(worker.managerSessionId) || !stringValue(worker.intercomTarget)) { + throw new Error("canonical worker ownership routing is incomplete"); + } + return { ...worker, canonicalIdentity: identity }; +} + +function exactBossRosterSession(sessions: TeamSession[], worker: StoredWorker): TeamSession | undefined { + const identity = worker.canonicalIdentity; + const target = stringValue(worker.intercomTarget); + const role = stringValue(worker.role); + const state = stringValue(worker.state); + if (!identity || !("bossRunId" in identity) || !target || !role || !state) return undefined; + const matches = sessions.filter((candidate) => candidate.id === target); + if (matches.length !== 1) return undefined; + const [session] = matches; + if (!session?.boss?.binding || session.boss.workerIdentity === undefined || session.boss.participantState === undefined) return undefined; try { - const parsed = JSON.parse(await readFile(join(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")) as { workers?: unknown }; - return Array.isArray(parsed.workers) ? parsed.workers as StoredWorker[] : []; - } catch { return []; } + const sessionIdentity = parseWorkerIdentityV2(session.boss.workerIdentity); + const sessionState = parseParticipantState(session.boss.participantState, "$.session.boss.participantState"); + const binding = session.boss.binding; + return ( + "bossRunId" in sessionIdentity + && session.id === target + && binding.sessionId === session.id + && binding.state === "active" + && binding.bossRunId === identity.bossRunId + && binding.participantId === identity.participantId + && binding.bindingEpoch === identity.bindingEpoch + && binding.role === role + && sessionIdentity.workerId === identity.workerId + && sessionIdentity.workerIncarnationId === identity.workerIncarnationId + && sessionIdentity.workerGeneration === identity.workerGeneration + && sessionIdentity.bossRunId === identity.bossRunId + && sessionIdentity.participantId === identity.participantId + && sessionIdentity.bindingEpoch === identity.bindingEpoch + && sessionState === state + ) ? session : undefined; + } catch { + return undefined; + } } + +async function readWorkers(agentDir: string): Promise<{ version: 1 | 2; workers: StoredWorker[] }> { + try { + const parsed: unknown = JSON.parse(await readFile(join(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("worker snapshot must be an object"); + const snapshot = parsed as { version?: unknown; workers?: unknown }; + if ((snapshot.version !== 1 && snapshot.version !== 2) || !Array.isArray(snapshot.workers)) throw new Error("unsupported worker snapshot version"); + if (snapshot.version === 1) return { version: 1, workers: snapshot.workers as StoredWorker[] }; + return { version: 2, workers: snapshot.workers.map(canonicalWorker) }; + } catch { + return { version: 1, workers: [] }; + } +} + export async function resolveIntercomTeam(input: { selfId: string; sessions: TeamSession[]; env?: NodeJS.ProcessEnv; agentDir?: string }): Promise { const env = input.env ?? process.env; - const workers = await readWorkers(input.agentDir ?? getAgentDirPath()); + const snapshot = await readWorkers(input.agentDir ?? getAgentDirPath()); + const workers = snapshot.workers; const workerId = stringValue(env.AGENT_INTERCOM_WORKER_ID); + const bossIdentity = bossIdentityFromEnvironment(env); const runId = stringValue(env.AGENT_INTERCOM_RUN_ID); - const current = workerId ? workers.find((worker) => stringValue(worker.id) === workerId && (!runId || stringValue(worker.runId) === runId)) : undefined; - const managerTarget = stringValue(current?.managerSessionId) ?? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID); + const currentMatches = workerId ? workers.filter((worker) => ( + stringValue(worker.id) === workerId + && (bossIdentity === undefined + ? (!runId || stringValue(worker.runId) === runId) + : snapshot.version === 2 + && worker.canonicalIdentity?.workerId === bossIdentity.workerId + && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId + && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration + && "bossRunId" in worker.canonicalIdentity + && "bossRunId" in bossIdentity + && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId + && worker.canonicalIdentity.participantId === bossIdentity.participantId + && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch) + )) : []; + const current = bossIdentity === undefined ? currentMatches[0] : currentMatches.length === 1 ? currentMatches[0] : undefined; + const currentTarget = stringValue(current?.intercomTarget); + const exactCurrentProjection = current !== undefined + && currentTarget === input.selfId + && workers.filter((worker) => stringValue(worker.id) === workerId).length === 1 + && workers.filter((worker) => stringValue(worker.intercomTarget) === currentTarget).length === 1 + && exactBossRosterSession(input.sessions, current) !== undefined; + + // Privileged Boss discovery is rooted in one exact current worker/session + // projection. A substituted self ID, ambiguous ID/target, or stale binding + // never unlocks a roster assembled from ambient same-run records. + if (bossIdentity !== undefined && !exactCurrentProjection) { + return { self: { id: input.selfId, ...(workerId ? { workerId } : {}), isManager: false }, coworkers: [] }; + } + + const managerTarget = stringValue(current?.managerSessionId) + ?? (bossIdentity === undefined ? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID) : undefined); const teamId = managerTarget ?? input.selfId; - const coworkers = workers.filter((worker) => worker.owned === true) + const currentRole = stringValue(current?.role); + const canDiscoverOwnedRoster = bossIdentity === undefined || currentRole === "manager" || currentRole === "controller"; + const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true) + .filter((worker) => bossIdentity === undefined || ( + snapshot.version === 2 + && worker.canonicalIdentity !== undefined + && "bossRunId" in worker.canonicalIdentity + && "bossRunId" in bossIdentity + && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId + )) .filter((worker) => stringValue(worker.managerSessionId) === teamId) - .filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? "")) + .filter((worker) => stringValue(worker.intercomTarget) !== managerTarget) + .filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")) .filter((worker) => stringValue(worker.id) !== workerId) .map((worker): TeamMember | undefined => { - const id = stringValue(worker.id); if (!id) return undefined; + const id = stringValue(worker.id); + if (!id) return undefined; const target = stringValue(worker.intercomTarget) ?? id; - return { id, target, ...(stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}), ...(stringValue(worker.role) ? { role: stringValue(worker.role) } : {}), ...(stringValue(worker.state) ? { state: stringValue(worker.state) } : {}), connected: connectedTo(input.sessions, target) }; + const connected = bossIdentity === undefined + ? connectedTo(input.sessions, target) + : exactBossRosterSession(input.sessions, worker) !== undefined; + if (!connected) return undefined; + return { + id, + target, + ...(stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}), + ...(stringValue(worker.role) ? { role: stringValue(worker.role) } : {}), + ...(stringValue(worker.state) ? { state: stringValue(worker.state) } : {}), + connected, + }; }).filter((member): member is TeamMember => Boolean(member)); - return { teamId, self: { id: input.selfId, ...(workerId ? { workerId } : {}), isManager: !managerTarget }, manager: managerTarget ? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } : { target: input.selfId, connected: true }, coworkers }; + + const managerWorker = managerTarget === undefined + ? undefined + : workers.find((worker) => ( + stringValue(worker.intercomTarget) === managerTarget + && (bossIdentity === undefined || ( + snapshot.version === 2 + && stringValue(worker.role) === "manager" + && worker.canonicalIdentity !== undefined + && "bossRunId" in worker.canonicalIdentity + && "bossRunId" in bossIdentity + && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId + )) + )); + const managerConnected = managerTarget === undefined + ? true + : bossIdentity === undefined + ? connectedTo(input.sessions, managerTarget) + : managerWorker !== undefined && exactBossRosterSession(input.sessions, managerWorker) !== undefined; + return { + teamId, + self: { id: input.selfId, ...(workerId ? { workerId } : {}), isManager: bossIdentity === undefined && !managerTarget }, + ...(managerTarget + ? { manager: { target: managerTarget, connected: managerConnected } } + : bossIdentity === undefined ? { manager: { target: input.selfId, connected: true } } : {}), + coworkers, + }; } + export function formatIntercomTeam(team: IntercomTeam): string { - const lines = [`Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}`]; + const lines = [ + `Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, + `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}`, + ]; if (!team.coworkers.length) lines.push("Coworkers: none"); - else { lines.push("Coworkers:"); for (const coworker of team.coworkers) { const metadata = [coworker.harness, coworker.role, coworker.state].filter(Boolean).join(", "); lines.push(`- ${coworker.id} target=${coworker.target}${metadata ? ` (${metadata})` : ""} [${coworker.connected ? "connected" : "not connected"}]`); } } + else { + lines.push("Coworkers:"); + for (const coworker of team.coworkers) { + const metadata = [coworker.harness, coworker.role, coworker.state].filter(Boolean).join(", "); + lines.push(`- ${coworker.id} target=${coworker.target}${metadata ? ` (${metadata})` : ""} [${coworker.connected ? "connected" : "not connected"}]`); + } + } return lines.join("\n"); } diff --git a/dist/bridge-daemon.mjs b/dist/bridge-daemon.mjs index 7aa629c..3ef54fc 100755 --- a/dist/bridge-daemon.mjs +++ b/dist/bridge-daemon.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=bridge-daemon sourceSha256=28cbe04c291ec9ca89e519b437e41d7a7c359f85cf99d2fcf3e6cb0f74dcee2c\n"); +process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=bridge-daemon sourceSha256=e3924d8a81ca3579d920e6938f77fe75f36c8eeb02d45b49b2d940b7a67b6410\n"); // codex/bridge-daemon.ts import { once } from "node:events"; @@ -13,6 +13,27 @@ import { randomBytes, createHash } from "node:crypto"; import net from "node:net"; import readline from "node:readline"; import { setTimeout as delay } from "node:timers/promises"; + +// codex/boss-client.ts +var HARDENED_BOSS_CODEX_DEFAULTS = Object.freeze({ + boss_participant: Object.freeze({ approvalPolicy: "untrusted", sandbox: "workspace-write" }), + boss_reviewer: Object.freeze({ approvalPolicy: "untrusted", sandbox: "read-only" }) +}); +var PROVIDER_AUTHORITY_UNAVAILABLE = "PROVIDER_AUTHORITY_UNAVAILABLE"; +var ProviderAuthorityUnavailableError = class extends Error { + constructor(bossClient) { + super(`${PROVIDER_AUTHORITY_UNAVAILABLE}: ${bossClient} requires a broker-owned, artifact-attested Codex provider executable`); + this.bossClient = bossClient; + this.name = "ProviderAuthorityUnavailableError"; + } + bossClient; + code = PROVIDER_AUTHORITY_UNAVAILABLE; +}; +function assertHardenedBossProviderAuthority(bossClient) { + if (bossClient !== void 0) throw new ProviderAuthorityUnavailableError(bossClient); +} + +// codex/app-server-client.ts var DEFAULT_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3; var MAX_WEBSOCKET_MESSAGE_BYTES = 16 * 1024 * 1024; var UNIX_WEBSOCKET_CONNECT_TIMEOUT_MS = 1e4; @@ -49,8 +70,9 @@ var CodexAppServerClient = class extends EventEmitter { pending = /* @__PURE__ */ new Map(); initialized = false; options; - constructor(options = {}) { + constructor(options = {}, protectedBossClient) { super(); + assertHardenedBossProviderAuthority(protectedBossClient); this.options = { command: options.command ?? "codex", args: options.args ?? ["app-server"], @@ -60,7 +82,8 @@ var CodexAppServerClient = class extends EventEmitter { startDaemon: options.startDaemon ?? false, startDaemonCommand: options.startDaemonCommand ?? "codex", startDaemonArgs: options.startDaemonArgs ?? ["app-server", "daemon", "start"], - requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS + requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, + env: options.env ?? process.env }; } setServerRequestHandler(handler) { @@ -71,6 +94,7 @@ var CodexAppServerClient = class extends EventEmitter { if (this.options.startDaemon) { const started = spawnSync(this.options.startDaemonCommand, this.options.startDaemonArgs, { encoding: "utf8", + env: this.options.env, stdio: ["ignore", "pipe", "pipe"] }); if (started.status !== 0) { @@ -84,7 +108,7 @@ var CodexAppServerClient = class extends EventEmitter { } const proc = spawn(this.options.command, this.options.args, { stdio: ["pipe", "pipe", "pipe"], - env: process.env + env: this.options.env }); this.proc = proc; this.rl = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity }); @@ -474,8 +498,9 @@ var WebSocketFrameDecoder = class { // codex/bridge-config.ts import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs"; -import { dirname, join as join2, resolve as resolve2 } from "node:path"; +import { dirname, join as join2, parse as parsePath, resolve as resolve2 } from "node:path"; import { cwd as processCwd } from "node:process"; +import { types as nodeUtilTypes2 } from "node:util"; // broker/paths.ts import { chmodSync, mkdirSync, readFileSync } from "fs"; @@ -547,11 +572,314 @@ function restrictIntercomRuntimeFile(filePath, platform = process.platform) { } } +// broker/boss-adapter.ts +import { + BOSS_CAPABILITY_FEATURE_DIGEST, + BOSS_CONTROL_ENVELOPE_VERSION, + BOSS_POLICY_PRINCIPAL_VERSION, + BOSS_POLICY_SEMANTICS_HASH, + BOSS_RUN_FEATURE, + BOSS_RUN_FEATURE_CONTRACT, + BOSS_RUN_FEATURE_SEMANTICS_HASH, + BOSS_RUN_FEATURE_VERSION, + BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + BROKER_FEATURE_ATTESTATION_VERSION, + INTERCOM_BASE_PROTOCOL_VERSION, + authorizeFeatureAware, + brokerFeatureSetHash, + parseBossControlEnvelope, + parseBossParticipantBinding, + parseBossParticipantCredentialEnvelope, + parseBossRunFeatureContract, + parseBrokerCapabilityAdvertisement, + parseParticipantState, + parseWorkerIdentityV2 +} from "@dataforxyz/agent-intercom-core/boss"; +import { + ContractValidationError, + assertExactKeys, + assertRecord, + canonicalJson +} from "@dataforxyz/agent-intercom-core/canonical"; +import { types as nodeUtilTypes } from "node:util"; +var BOSS_ADVERTISEMENT_PREDICATES = [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth" +]; +var DORMANT_BOSS_ADVERTISEMENT_READINESS = Object.freeze({ + protectedProvider: false, + brokerIdentity: false, + credentialRegistry: false, + authorityTransitions: false, + participantHealth: false +}); +var ORDINARY_SESSION_REGISTRATION_KEYS = [ + "cwd", + "model", + "pid", + "startedAt", + "lastActivity" +]; +var OPTIONAL_SESSION_REGISTRATION_KEYS = ["name", "status", "runtimeInstanceId"]; +function assertBossCanonicalData(value, path = "$", seen = /* @__PURE__ */ new WeakSet()) { + if (typeof value !== "object" || value === null) return; + if (nodeUtilTypes.isProxy(value)) { + throw new ContractValidationError(path, "proxies are not supported"); + } + if (seen.has(value)) throw new ContractValidationError(path, "cyclic values are not supported"); + seen.add(value); + if (Array.isArray(value)) { + if (Object.getPrototypeOf(value) !== Array.prototype) { + throw new ContractValidationError(path, "must use the exact Array prototype"); + } + const ownKeys = Reflect.ownKeys(value); + const expectedKeys = /* @__PURE__ */ new Set(["length"]); + for (let index = 0; index < value.length; index += 1) expectedKeys.add(String(index)); + if (ownKeys.length !== expectedKeys.size || ownKeys.some((key) => !expectedKeys.has(key))) { + throw new ContractValidationError(path, "must be a dense array without symbols or extra properties"); + } + for (let index = 0; index < value.length; index += 1) { + if (!Object.hasOwn(value, index)) { + throw new ContractValidationError(`${path}[${index}]`, "sparse array holes are not supported"); + } + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}[${index}]`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}[${index}]`, seen); + } + return; + } + if (Object.getPrototypeOf(value) !== Object.prototype) { + throw new ContractValidationError(path, "must use the exact Object prototype"); + } + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") throw new ContractValidationError(path, "symbol properties are not supported"); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}.${key}`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}.${key}`, seen); + } +} +function parseBossAdvertisementReadiness(value) { + assertBossCanonicalData(value, "$.readiness"); + assertRecord(value); + assertExactKeys(value, BOSS_ADVERTISEMENT_PREDICATES); + const parsed = {}; + for (const predicate of BOSS_ADVERTISEMENT_PREDICATES) { + const enabled = ownDataValue(value, predicate); + if (typeof enabled !== "boolean") { + throw new ContractValidationError(`$.readiness.${predicate}`, "must be a boolean"); + } + parsed[predicate] = enabled; + } + return parsed; +} +function missingBossAdvertisementPredicates(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + const parsed = parseBossAdvertisementReadiness(readiness); + return BOSS_ADVERTISEMENT_PREDICATES.filter((predicate) => parsed[predicate] !== true); +} +function bossCapabilityAdvertisement(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + if (missingBossAdvertisementPredicates(readiness).length > 0) return void 0; + const features = [{ + version: BROKER_FEATURE_ATTESTATION_VERSION, + feature: BOSS_RUN_FEATURE, + featureVersion: BOSS_RUN_FEATURE_VERSION, + semanticsHash: BOSS_RUN_FEATURE_SEMANTICS_HASH, + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }]; + return parseBrokerCapabilityAdvertisement({ + baseProtocolVersion: INTERCOM_BASE_PROTOCOL_VERSION, + features, + protocolFeatureContractHash: BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + featureSetHash: brokerFeatureSetHash(features), + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }); +} +function optionalOwnDataValue(value, key) { + assertRecord(value); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0) return void 0; + if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`$.${key}`, "must be an own enumerable data property"); + } + return descriptor.value; +} +function ownDataValue(value, key) { + const result = optionalOwnDataValue(value, key); + if (result === void 0) throw new ContractValidationError(`$.${key}`, "is required"); + return result; +} +function parseBossParticipantRegistrationMetadata(value) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys(value, ["featureContract", "credential"]); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly negotiate boss-run-v1 over base protocol v3"); + } + const credential = parseBossParticipantCredentialEnvelope(ownDataValue(value, "credential")); + if (credential.namespace !== featureContract.feature) { + throw new ContractValidationError("$.credential.namespace", "must match the negotiated feature namespace"); + } + return { featureContract, credential }; +} +function exactRegistrationKind(session, value) { + assertBossCanonicalData(session, "$.session"); + assertRecord(session); + const boss = optionalOwnDataValue(session, "boss"); + if (boss === void 0) { + if (value === void 0) return "ordinary"; + throw new ContractValidationError("$.registrationKind", "must be absent when Boss metadata is absent"); + } + if (value !== "boss") { + throw new ContractValidationError("$.registrationKind", "must be boss when Boss metadata is present"); + } + return "boss"; +} +function parseExactRegistrationFrame(value) { + assertBossCanonicalData(value, "$.register"); + assertRecord(value); + const session = ownDataValue(value, "session"); + assertRecord(session); + const registrationKind = optionalOwnDataValue(value, "registrationKind"); + const kind = exactRegistrationKind(session, registrationKind); + if (kind === "ordinary") { + assertExactKeys(value, ["type", "protocol", "version", "session"], ["sessionId", "stateId", "access"]); + assertExactKeys(session, ORDINARY_SESSION_REGISTRATION_KEYS, OPTIONAL_SESSION_REGISTRATION_KEYS); + } else { + assertExactKeys(value, ["type", "registrationKind", "protocol", "version", "session"], ["sessionId", "stateId"]); + assertExactKeys(session, [...ORDINARY_SESSION_REGISTRATION_KEYS, "boss"], OPTIONAL_SESSION_REGISTRATION_KEYS); + parseBossParticipantRegistrationMetadata(ownDataValue(session, "boss")); + } + if (ownDataValue(value, "type") !== "register") { + throw new ContractValidationError("$.register.type", "must be register"); + } + return value; +} +function parseExactRegisteredFrame(value, expected) { + assertBossCanonicalData(value, "$.registered"); + assertRecord(value); + if (expected === "boss") { + assertExactKeys(value, ["type", "registrationKind", "sessionId", "protocol", "version", "capabilities", "boss"]); + if (ownDataValue(value, "registrationKind") !== "boss") { + throw new ContractValidationError("$.registered.registrationKind", "must be boss"); + } + const sessionId = ownDataValue(value, "sessionId"); + if (typeof sessionId !== "string" || sessionId.length === 0) { + throw new ContractValidationError("$.registered.sessionId", "must be a non-empty string"); + } + const advertisement = parseBrokerCapabilityAdvertisement(ownDataValue(value, "capabilities")); + const expectedAdvertisement = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true + }); + const bossFeature = advertisement.features.find((feature) => feature.feature === BOSS_RUN_FEATURE); + if (bossFeature === void 0 || canonicalJson(bossFeature) !== canonicalJson(expectedAdvertisement.features[0]) || advertisement.baseProtocolVersion !== expectedAdvertisement.baseProtocolVersion || advertisement.protocolFeatureContractHash !== expectedAdvertisement.protocolFeatureContractHash || advertisement.controlEnvelopeVersion !== expectedAdvertisement.controlEnvelopeVersion || advertisement.capabilityDigest !== expectedAdvertisement.capabilityDigest) throw new ContractValidationError("$.registered.capabilities", "must exactly echo the requested boss-run-v1 contract"); + parseBossParticipantBindingMetadata(ownDataValue(value, "boss"), sessionId); + } else if (expected === "ordinary-remote") { + assertExactKeys(value, ["type", "sessionId", "protocol", "version", "remoteAccess", "access"]); + } else { + assertExactKeys(value, ["type", "sessionId", "protocol", "version"]); + } + if (ownDataValue(value, "type") !== "registered") { + throw new ContractValidationError("$.registered.type", "must be registered"); + } + return value; +} +function parseBossParticipantBindingMetadata(value, expectedSessionId) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys( + value, + ["featureContract", "binding", "brokerIdentityVerified"], + ["assignedParticipantIds", "requestingPrincipalId", "workerIdentity", "participantState"] + ); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly bind boss-run-v1 over base protocol v3"); + } + const binding = parseBossParticipantBinding(ownDataValue(value, "binding")); + if (ownDataValue(value, "brokerIdentityVerified") !== true) { + throw new ContractValidationError("$.brokerIdentityVerified", "must be true for a broker-owned Boss binding"); + } + if (expectedSessionId !== void 0 && binding.sessionId !== expectedSessionId) { + throw new ContractValidationError("$.binding.sessionId", "must match the registered intercom session"); + } + const rawAssignedParticipantIds = optionalOwnDataValue(value, "assignedParticipantIds"); + let assignedParticipantIds; + if (rawAssignedParticipantIds !== void 0) { + if (binding.role !== "manager" || !Array.isArray(rawAssignedParticipantIds) || rawAssignedParticipantIds.some((entry) => typeof entry !== "string" || entry.length === 0) || new Set(rawAssignedParticipantIds).size !== rawAssignedParticipantIds.length) { + throw new ContractValidationError("$.assignedParticipantIds", "must be a unique participant list present only for a Manager"); + } + assignedParticipantIds = rawAssignedParticipantIds; + } + if (binding.role === "manager" && assignedParticipantIds === void 0) { + throw new ContractValidationError("$.assignedParticipantIds", "is required for a Manager policy binding"); + } + const rawRequestingPrincipalId = optionalOwnDataValue(value, "requestingPrincipalId"); + if (binding.role === "council" !== (typeof rawRequestingPrincipalId === "string" && rawRequestingPrincipalId.length > 0)) { + throw new ContractValidationError("$.requestingPrincipalId", "is required exactly for a Council policy binding"); + } + const requestingPrincipalId = typeof rawRequestingPrincipalId === "string" ? rawRequestingPrincipalId : void 0; + const rawWorkerIdentity = optionalOwnDataValue(value, "workerIdentity"); + const rawParticipantState = optionalOwnDataValue(value, "participantState"); + if (rawWorkerIdentity === void 0 !== (rawParticipantState === void 0)) { + throw new ContractValidationError("$.workerIdentity", "workerIdentity and participantState must be supplied together"); + } + const workerIdentity = rawWorkerIdentity === void 0 ? void 0 : parseWorkerIdentityV2(rawWorkerIdentity); + const participantState = rawParticipantState === void 0 ? void 0 : parseParticipantState(rawParticipantState, "$.participantState"); + if (workerIdentity !== void 0 && (!("bossRunId" in workerIdentity) || workerIdentity.bossRunId !== binding.bossRunId || workerIdentity.participantId !== binding.participantId || workerIdentity.bindingEpoch !== binding.bindingEpoch)) throw new ContractValidationError("$.workerIdentity", "must match the broker-owned participant binding"); + return { + featureContract, + binding, + brokerIdentityVerified: true, + ...assignedParticipantIds === void 0 ? {} : { assignedParticipantIds: [...assignedParticipantIds] }, + ...requestingPrincipalId === void 0 ? {} : { requestingPrincipalId }, + ...workerIdentity === void 0 ? {} : { workerIdentity, participantState } + }; +} +var BOSS_CONTROL_KIND_BY_TYPE = { + "boss.assignment.created": "assignment_request", + "boss.assignment.accepted": "assignment_response", + "boss.assignment.checkpoint": "assignment_response", + "boss.assignment.submitted": "assignment_response", + "boss.assignment.rejected": "assignment_response", + "boss.assignment.cancelled": "lifecycle", + "boss.staffing.requested": "staffing", + "boss.staffing.resolved": "staffing", + "boss.review.requested": "review_request", + "boss.review.submitted": "review_result", + "boss.council.requested": "review_request", + "boss.council.submitted": "review_result", + "boss.proof.submitted": "proof", + "boss.worker.health": "health", + "boss.worker.blocked": "health", + "boss.worker.failed": "health", + "boss.worker.notice": "lifecycle", + "boss.worker.notice_delivery_failed": "lifecycle", + "boss.decision.required": "decision" +}; +function bossControlKind(envelopeValue) { + assertBossCanonicalData(envelopeValue); + const envelope = parseBossControlEnvelope(envelopeValue); + return { envelope, controlKind: BOSS_CONTROL_KIND_BY_TYPE[envelope.type] }; +} + // codex/bridge-config.ts var DEFAULT_BRIDGE_CONFIG_PATH = join2(getIntercomDirPath(), "codex-bridge.json"); var DEFAULT_BRIDGE_STATE_PATH = join2(getIntercomDirPath(), "codex-bridge-state.json"); function isRecord(value) { - return typeof value === "object" && value !== null && !Array.isArray(value); + return typeof value === "object" && value !== null && !Array.isArray(value) && !nodeUtilTypes2.isProxy(value); } function optionalString(value, field) { if (value === void 0 || value === null) return void 0; @@ -564,11 +892,126 @@ function requireString(value, field) { if (!result) throw new Error(`${field} must be a non-empty string`); return result; } +function parseHardenedBossClientKind(value, field) { + if (value === void 0 || value === null) return void 0; + if (value === "boss_participant" || value === "boss_reviewer") return value; + throw new Error(`${field} must be boss_participant or boss_reviewer`); +} +function sandboxType(value) { + if (!isRecord(value)) return void 0; + return typeof value.type === "string" ? value.type : void 0; +} +function assertHardenedBossAgentConfig(agent) { + if (nodeUtilTypes2.isProxy(agent)) throw new Error("Hardened Boss agent config must not be a proxy"); + assertBossCanonicalData(agent, "$.agent"); + if (agent.bossClient === void 0) return; + if (agent.sandboxPolicy !== void 0 && !isRecord(agent.sandboxPolicy)) { + throw new Error(`${agent.bossClient} sandboxPolicy must be a plain object`); + } + if (agent.approvalPolicy !== void 0 && typeof agent.approvalPolicy !== "string") { + throw new Error(`${agent.bossClient} approvalPolicy must be a string`); + } + const type = sandboxType(agent.sandboxPolicy); + if (type === "dangerFullAccess" || type === "danger-full-access") { + throw new Error(`${agent.bossClient} cannot use danger-full-access`); + } + if (agent.approvalPolicy === "never") { + throw new Error(`${agent.bossClient} cannot disable approval checks`); + } + if (agent.bossClient === "boss_reviewer" && type !== void 0 && type !== "readOnly" && type !== "read-only") { + throw new Error("boss_reviewer must use a read-only sandbox"); + } + const canonicalCwd = resolve2(agent.cwd); + if (agent.bossClient === "boss_participant" && canonicalCwd === parsePath(canonicalCwd).root) { + throw new Error("boss_participant workspace root must not be a filesystem root"); + } + if (isRecord(agent.sandboxPolicy)) { + assertBossCanonicalData(agent.sandboxPolicy, "$.agent.sandboxPolicy"); + const allowedKeys = type === "workspaceWrite" || type === "workspace-write" ? /* @__PURE__ */ new Set(["type", "writableRoots", "networkAccess"]) : /* @__PURE__ */ new Set(["type", "networkAccess"]); + const keys = Reflect.ownKeys(agent.sandboxPolicy); + if (keys.some((key) => typeof key !== "string" || !allowedKeys.has(key))) { + throw new Error(`${agent.bossClient} sandboxPolicy contains unsupported capability fields`); + } + if (agent.sandboxPolicy.networkAccess !== false) { + throw new Error(`${agent.bossClient} networkAccess must be false`); + } + if (type !== "readOnly" && type !== "read-only" && type !== "workspaceWrite" && type !== "workspace-write") { + throw new Error(`${agent.bossClient} sandboxPolicy type is unsupported`); + } + } + if (isRecord(agent.sandboxPolicy) && (type === "workspaceWrite" || type === "workspace-write")) { + const roots = agent.sandboxPolicy.writableRoots; + assertBossCanonicalData(roots, "$.agent.sandboxPolicy.writableRoots"); + if (!Array.isArray(roots) || nodeUtilTypes2.isProxy(roots) || roots.some((root) => typeof root !== "string")) { + throw new Error(`${agent.bossClient} writableRoots must be a dense string array`); + } + if (agent.bossClient === "boss_reviewer" || roots.length !== 1 || resolve2(roots[0]) !== canonicalCwd) { + throw new Error(`${agent.bossClient} writable roots must be restricted to the agent cwd`); + } + } + if (agent.bossClient === "boss_participant") { + throw new Error("boss_participant requires unavailable broker-owned assigned workspace authority"); + } +} +function bridgeAgentApprovalPolicy(agent) { + return agent.approvalPolicy ?? (agent.bossClient === void 0 ? "never" : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].approvalPolicy); +} +function bridgeAgentDefaultSandbox(agent) { + return agent.bossClient === void 0 ? void 0 : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].sandbox; +} +function assertHardenedBossBridgeConfig(config) { + assertBossCanonicalData(config, "$.bridgeConfig"); + if (nodeUtilTypes2.isProxy(config) || nodeUtilTypes2.isProxy(config.agents) || !Array.isArray(config.agents)) { + throw new Error("Bridge config and agents must be plain non-proxy data"); + } + for (let index = 0; index < config.agents.length; index += 1) { + if (!Object.hasOwn(config.agents, index)) throw new Error("Bridge agents must not be sparse"); + const agent = config.agents[index]; + if (typeof agent !== "object" || agent === null || Array.isArray(agent) || nodeUtilTypes2.isProxy(agent)) { + throw new Error("Bridge agents must be plain non-proxy objects"); + } + } + if (!config.agents.some((agent) => agent.bossClient !== void 0)) return; + if (config.appServer !== void 0) { + assertBossCanonicalData(config.appServer, "$.appServer"); + if (nodeUtilTypes2.isProxy(config.appServer)) throw new Error("Hardened Boss app-server config must not be a proxy"); + if (config.appServer.command !== void 0 || config.appServer.startDaemonCommand !== void 0) { + throw new Error("Hardened Boss bridge cannot use caller-provided app-server commands"); + } + } + for (const args of [config.appServer?.args, config.appServer?.startDaemonArgs]) { + if (!args) continue; + assertBossCanonicalData(args, "$.appServer.argv"); + if (nodeUtilTypes2.isProxy(args) || args.some((arg) => typeof arg !== "string")) throw new Error("Hardened Boss bridge arguments must be dense string arrays"); + for (const arg of args) { + if (arg === "--") { + continue; + } + if (arg.length > 2 && arg.startsWith("-C")) { + throw new Error("Hardened Boss bridge cannot pass launch escape -C to app-server"); + } + const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; + if (["-c", "--config", "-p", "--profile", "--enable", "--disable"].includes(optionName) || optionName.startsWith("-c") && optionName !== "-C" || optionName.startsWith("-p")) { + throw new Error(`Hardened Boss bridge cannot pass raw ${optionName} or profile configuration to app-server`); + } + if (["--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-hook-trust", "--yolo", "--add-dir", "--cd", "-C"].includes(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + if (["--sandbox", "--ask-for-approval"].includes(optionName) || optionName === "-s" || optionName === "-a" || optionName.startsWith("-s") || optionName.startsWith("-a")) { + throw new Error(`Hardened Boss bridge cannot pass policy override ${optionName} to app-server`); + } + if (optionName.startsWith("-") && /(?:yolo|danger|bypass)/i.test(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + } + } + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); +} function normalizeAgent(raw, index) { if (!isRecord(raw)) throw new Error(`agents[${index}] must be an object`); const id = requireString(raw.id, `agents[${index}].id`); const name = optionalString(raw.name, `agents[${index}].name`) ?? id; - return { + const agent = { id, name, cwd: resolve2(optionalString(raw.cwd, `agents[${index}].cwd`) ?? processCwd()), @@ -576,20 +1019,27 @@ function normalizeAgent(raw, index) { threadId: optionalString(raw.threadId, `agents[${index}].threadId`), instructions: optionalString(raw.instructions, `agents[${index}].instructions`), approvalPolicy: raw.approvalPolicy, - sandboxPolicy: raw.sandboxPolicy + sandboxPolicy: raw.sandboxPolicy, + bossClient: parseHardenedBossClientKind(raw.bossClient, `agents[${index}].bossClient`) }; + assertHardenedBossAgentConfig(agent); + return agent; } function defaultBridgeConfig(env = process.env) { const id = env.CODEX_INTERCOM_BRIDGE_ID?.trim() || "codex-worker"; + const bossClient = parseHardenedBossClientKind(env.CODEX_INTERCOM_BOSS_CLIENT?.trim(), "CODEX_INTERCOM_BOSS_CLIENT"); + const agent = { + id, + name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, + cwd: resolve2(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), + model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || void 0, + instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || void 0, + ...bossClient === void 0 ? {} : { bossClient } + }; + assertHardenedBossAgentConfig(agent); return { statePath: env.CODEX_INTERCOM_BRIDGE_STATE?.trim() || DEFAULT_BRIDGE_STATE_PATH, - agents: [{ - id, - name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, - cwd: resolve2(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), - model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || void 0, - instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || void 0 - }] + agents: [agent] }; } function loadBridgeConfig(path = process.env.CODEX_INTERCOM_BRIDGE_CONFIG || DEFAULT_BRIDGE_CONFIG_PATH) { @@ -637,166 +1087,11 @@ function saveBridgeState(path, state) { import { EventEmitter as EventEmitter2 } from "events"; import net2 from "net"; import { randomUUID as randomUUID2 } from "crypto"; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy.js -var POLICY_SEMANTICS_VERSION = 2; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy-vectors.js -var localRoot = { - id: "local-root", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-root" -}; -var localPeer = { - id: "local-peer", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-peer" -}; -var remoteManager = { - id: "remote-manager", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "local-root", - rootSessionId: "local-root" -}; -var remoteChild = { - id: "remote-child", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var remoteSibling = { - id: "remote-sibling", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var POLICY_VECTORS = [ - { - name: "local sessions remain public", - principals: [localRoot, localPeer], - actorId: "local-root", - action: "send", - targetId: "local-peer", - expectedAllowed: true, - expectedReasonOrCode: "local-public" - }, - { - name: "remote manager can reach direct local parent", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "local parent can reach direct remote child", - principals: [localRoot, remoteManager], - actorId: "local-root", - action: "ask", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "remote child can reach its local root through the ancestor chain", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-chain" - }, - { - name: "remote siblings cannot communicate in phase one", - principals: [localRoot, remoteManager, remoteChild, remoteSibling], - actorId: "remote-child", - action: "discover", - targetId: "remote-sibling", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "unrelated local session cannot discover remote principal", - principals: [localRoot, localPeer, remoteManager], - actorId: "local-peer", - action: "discover", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal cannot reach unrelated local session", - principals: [localRoot, localPeer, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-peer", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote manager may inspect its descendant subtree", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-manager", - action: "inspect_tree", - targetId: "remote-child", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-control" - }, - { - name: "remote child cannot revoke its ancestor", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "revoke", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal may request attenuated delegation under itself", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "delegate_child", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "self" - }, - { - name: "revoked principal cannot communicate", - principals: [localRoot, { ...remoteManager, state: "revoked" }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: false, - expectedReasonOrCode: "REVOKED_PRINCIPAL" - }, - { - name: "stale actor generation cannot send", - principals: [localRoot, { ...remoteManager, generation: 2 }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - context: { actorGeneration: 1 }, - expectedAllowed: false, - expectedReasonOrCode: "STALE_GENERATION" - } -]; -var POLICY_SEMANTICS_HASH = "f3b00e503631bc91123aedfbcf1df72cc9913e1893c09728b2c598f3dcdfdfe0"; +import { POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION } from "@dataforxyz/agent-intercom-core"; +import { + BOSS_RUN_FEATURE as BOSS_RUN_FEATURE2, + parseBrokerCapabilityAdvertisement as parseBrokerCapabilityAdvertisement2 +} from "@dataforxyz/agent-intercom-core/boss"; // broker/framing.ts var MAX_FRAME_BYTES = 1024 * 1024; @@ -977,8 +1272,159 @@ var PersistentOutboundOutbox = class { } }; +// boss-control-outbox.ts +import { createHash as createHash3 } from "node:crypto"; +import { chmodSync as chmodSync3, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync3 } from "node:fs"; +import { join as join4 } from "node:path"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; +import { parseBossControlEnvelope as parseBossControlEnvelope2 } from "@dataforxyz/agent-intercom-core/boss"; +var BOSS_CONTROL_OUTBOX_VERSION = 2; +var MAX_BOSS_CONTROL_OUTBOX_ENTRIES = 256; +function scope(envelope) { + return canonicalHash("agent-intercom-codex/boss-control/outbox-scope/v1", { + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey + }); +} +function fingerprint2(to, envelope) { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/outbox-request/v1", { to, envelope: stableEnvelope }); +} +function exactKeys(value, required, optional = []) { + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + const keys = Reflect.ownKeys(value); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseEntry(value) { + assertBossCanonicalData(value, "$.bossControlOutbox.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss outbox entry"); + const entry = value; + if (!exactKeys(entry, ["to", "envelope", "scope", "fingerprint", "queuedAt", "state"], ["deliveryId"])) { + throw new Error("Invalid Boss outbox entry fields"); + } + if (typeof entry.to !== "string" || entry.to.length === 0 || typeof entry.scope !== "string" || !/^[a-f0-9]{64}$/.test(entry.scope) || typeof entry.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(entry.fingerprint) || typeof entry.queuedAt !== "number" || !Number.isSafeInteger(entry.queuedAt) || entry.state !== "queued" && entry.state !== "accepted" || entry.state === "queued" && Object.hasOwn(entry, "deliveryId") || entry.state === "accepted" && (!Object.hasOwn(entry, "deliveryId") || typeof entry.deliveryId !== "string" || entry.deliveryId.length === 0)) throw new Error("Invalid Boss outbox entry binding"); + const envelope = parseBossControlEnvelope2(entry.envelope); + if (entry.scope !== scope(envelope) || entry.fingerprint !== fingerprint2(entry.to, envelope)) { + throw new Error("Boss outbox entry canonical binding mismatch"); + } + return { + to: entry.to, + envelope, + scope: entry.scope, + fingerprint: entry.fingerprint, + queuedAt: entry.queuedAt, + state: entry.state, + ...entry.deliveryId === void 0 ? {} : { deliveryId: entry.deliveryId } + }; +} +function fileName2(sessionId) { + return `${createHash3("sha256").update(sessionId).digest("hex")}.json`; +} +var PersistentBossControlOutbox = class { + path; + state; + constructor(sessionId, intercomDir = getIntercomDirPath()) { + ensureIntercomRuntimeDir(intercomDir); + const directory = join4(intercomDir, "boss-control-outbox"); + mkdirSync4(directory, { recursive: true, mode: INTERCOM_DIR_MODE }); + if (process.platform !== "win32") chmodSync3(directory, INTERCOM_DIR_MODE); + this.path = join4(directory, fileName2(sessionId)); + this.state = this.load(); + } + list() { + return structuredClone(this.state.entries); + } + find(idempotencyKey) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + return entry === void 0 ? void 0 : structuredClone(entry); + } + enqueue(to, envelopeValue) { + if (typeof to !== "string" || to.length === 0) throw new Error("Boss target session ID is required"); + assertBossCanonicalData(envelopeValue, "$.envelope"); + const envelope = parseBossControlEnvelope2(envelopeValue); + const candidateScope = scope(envelope); + const candidateFingerprint = fingerprint2(to, envelope); + const existing = this.state.entries.find((entry) => entry.scope === candidateScope); + if (existing) { + if (existing.fingerprint !== candidateFingerprint) { + throw new Error(`Boss idempotency key ${envelope.idempotencyKey} is queued with a different canonical request`); + } + if (existing.envelope.messageId !== envelope.messageId) { + existing.envelope = envelope; + existing.queuedAt = Date.now(); + this.persist(); + } + return "existing"; + } + if (this.state.entries.some((entry) => entry.envelope.messageId === envelope.messageId)) { + throw new Error(`Boss message ID ${envelope.messageId} is queued with a different idempotency scope`); + } + if (this.state.entries.length >= MAX_BOSS_CONTROL_OUTBOX_ENTRIES) throw new Error("Durable Boss control outbox is full"); + this.state.entries.push({ + to, + envelope, + scope: candidateScope, + fingerprint: candidateFingerprint, + queuedAt: Date.now(), + state: "queued" + }); + this.persist(); + return "added"; + } + markAccepted(idempotencyKey, messageId, deliveryId) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (!entry || entry.envelope.messageId !== messageId || !deliveryId) { + throw new Error("Boss acknowledgement does not match the durable outbox binding"); + } + if (entry.state === "accepted") { + if (entry.deliveryId !== deliveryId) throw new Error("Boss acknowledgement changed the durable deliveryId"); + return "already-accepted"; + } + entry.state = "accepted"; + entry.deliveryId = deliveryId; + this.persist(); + return "accepted"; + } + removeCorrelated(idempotencyKey, messageId, deliveryId) { + const index = this.state.entries.findIndex((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (index < 0) throw new Error("Boss terminal result has no durable outbox binding"); + const entry = this.state.entries[index]; + if (entry.envelope.messageId !== messageId) throw new Error("Boss terminal result messageId does not match the durable caller"); + if (deliveryId === void 0) { + if (entry.state !== "queued") throw new Error("Boss post-acceptance failure omitted the durable deliveryId"); + } else if (entry.state !== "accepted" || entry.deliveryId !== deliveryId) { + throw new Error("Boss terminal result arrived before the matching durable acknowledgement"); + } + this.state.entries.splice(index, 1); + this.persist(); + } + load() { + if (!existsSync3(this.path)) return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: [] }; + try { + const parsed = JSON.parse(readFileSync4(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlOutbox"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed; + if (!exactKeys(state, ["version", "entries"]) || state.version !== BOSS_CONTROL_OUTBOX_VERSION || !Array.isArray(state.entries)) { + throw new Error("invalid Boss outbox state"); + } + return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: state.entries.map(parseEntry) }; + } catch (error) { + const corruptPath = `${this.path}.corrupt-${Date.now()}`; + renameSync3(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control outbox was corrupt and quarantined at ${corruptPath}`, { cause: error }); + } + } + persist() { + writeDurableJson(this.path, this.state); + } +}; + // broker/access-credential.ts -import { readFileSync as readFileSync4 } from "fs"; +import { readFileSync as readFileSync5 } from "fs"; var ACCESS_CREDENTIAL_ENV = "AGENT_INTERCOM_ACCESS_CREDENTIAL_PATH"; var ACCESS_CREDENTIAL_VERSION = 1; function nonEmptyString(value) { @@ -987,7 +1433,7 @@ function nonEmptyString(value) { function loadRemoteAccessCredential(env = process.env) { const path = env[ACCESS_CREDENTIAL_ENV]?.trim(); if (!path) return void 0; - const parsed = JSON.parse(readFileSync4(path, "utf8")); + const parsed = JSON.parse(readFileSync5(path, "utf8")); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error(`Invalid Agent Intercom access credential at ${path}`); } @@ -1020,7 +1466,49 @@ function writeRemoteSessionCredential(path, sessionId, metadata) { }); } +// broker/boss-control-ledger.ts +import { canonicalJson as canonicalJson2 } from "@dataforxyz/agent-intercom-core/canonical"; +var BOSS_CONTROL_FAILURE_CODES = /* @__PURE__ */ new Set([ + "INVALID_CONTROL", + "IDEMPOTENCY_CONFLICT", + "SESSION_NOT_FOUND", + "POLICY_DENIED", + "RECIPIENT_DISCONNECTED", + "DELIVERY_TIMEOUT" +]); +function exactStringKeys(value, required, optional = []) { + const keys = Reflect.ownKeys(value); + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseBossControlResult(value) { + assertBossCanonicalData(value, "$.bossControlResult"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control result must be an exact plain object"); + } + const result = value; + const base = typeof result.requestId === "string" && result.requestId.length > 0 && result.messageId === result.requestId && typeof result.idempotencyKey === "string" && result.idempotencyKey.length > 0; + if (!base || result.type !== "boss_control_result") throw new Error("Invalid Boss control result binding"); + if (result.status === "delivered" && result.delivered === true && typeof result.deliveryId === "string" && result.deliveryId.length > 0 && exactStringKeys(result, ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "deliveryId"])) return result; + if (result.status === "rejected" && result.delivered === false && typeof result.code === "string" && BOSS_CONTROL_FAILURE_CODES.has(result.code) && typeof result.reason === "string" && result.reason.length > 0 && (!Object.hasOwn(result, "deliveryId") || typeof result.deliveryId === "string" && result.deliveryId.length > 0) && exactStringKeys( + result, + ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "code", "reason"], + ["deliveryId"] + )) return result; + throw new Error("Invalid Boss control result discriminant"); +} +function parseBossControlAck(value) { + assertBossCanonicalData(value, "$.bossControlAck"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control acknowledgement must be an exact plain object"); + } + const ack = value; + if (!exactStringKeys(ack, ["type", "requestId", "messageId", "idempotencyKey", "status", "deliveryId"]) || ack.type !== "boss_control_ack" || typeof ack.requestId !== "string" || ack.requestId.length === 0 || ack.messageId !== ack.requestId || typeof ack.idempotencyKey !== "string" || ack.idempotencyKey.length === 0 || ack.status !== "accepted" || typeof ack.deliveryId !== "string" || ack.deliveryId.length === 0) throw new Error("Invalid Boss control acknowledgement discriminant"); + return ack; +} + // broker/client.ts +import { types as nodeUtilTypes3 } from "node:util"; function toError(error) { return error instanceof Error ? error : new Error(String(error)); } @@ -1090,6 +1578,13 @@ function isSessionInfo(value) { for (const field of ["depth", "maxDepth", "maxChildren"]) { if (session[field] !== void 0 && (typeof session[field] !== "number" || !Number.isSafeInteger(session[field]))) return false; } + if (session.boss !== void 0) { + try { + parseBossParticipantBindingMetadata(session.boss, session.id); + } catch { + return false; + } + } return true; } function isRemoteAccessMetadata(value) { @@ -1103,8 +1598,12 @@ var IntercomClient = class extends EventEmitter2 { pendingSends = /* @__PURE__ */ new Map(); pendingLists = /* @__PURE__ */ new Map(); pendingAskControls = /* @__PURE__ */ new Map(); + pendingBossControls = /* @__PURE__ */ new Map(); outbox = null; + bossControlOutbox = null; remoteAccessCredential; + requestedBossRegistration; + _bossBinding; disconnecting = false; disconnectError = null; failPending(error) { @@ -1121,6 +1620,11 @@ var IntercomClient = class extends EventEmitter2 { pending.resolve(false); } this.pendingAskControls.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + pending.reject(error); + } + this.pendingBossControls.clear(); } get sessionId() { return this._sessionId; @@ -1128,6 +1632,12 @@ var IntercomClient = class extends EventEmitter2 { get outboxSize() { return this.outbox?.list().length ?? 0; } + get bossBinding() { + return this._bossBinding; + } + get bossControlOutboxSize() { + return this.bossControlOutbox?.list().length ?? 0; + } isConnected() { const socket = this.socket; return Boolean(socket && this._sessionId && !this.disconnecting && !socket.destroyed && !socket.writableEnded && socket.writable); @@ -1149,6 +1659,19 @@ var IntercomClient = class extends EventEmitter2 { if (this.socket) { return Promise.reject(new Error("Already connected")); } + try { + const canonicalSession = parseExactRegistrationFrame({ + type: "register", + ...typeof session === "object" && session !== null && !nodeUtilTypes3.isProxy(session) && Object.getOwnPropertyDescriptor(session, "boss") !== void 0 ? { registrationKind: "boss" } : {}, + protocol: INTERCOM_PROTOCOL_NAME, + version: INTERCOM_PROTOCOL_VERSION, + session + }).session; + this.requestedBossRegistration = session.boss === void 0 ? void 0 : parseBossParticipantRegistrationMetadata(session.boss); + if (canonicalSession !== session) throw new Error("Registration session identity changed during validation"); + } catch (error) { + return Promise.reject(toError(error)); + } return new Promise((resolve4, reject) => { let socket; let target; @@ -1203,6 +1726,8 @@ var IntercomClient = class extends EventEmitter2 { this.socket = null; } this._sessionId = null; + this._bossBinding = void 0; + this.requestedBossRegistration = void 0; this.disconnectError = null; if (connectionEstablished && !wasDisconnecting) { this.emit("disconnected", disconnectError); @@ -1248,6 +1773,7 @@ var IntercomClient = class extends EventEmitter2 { try { writeMessage(socket, { type: "register", + ...session.boss === void 0 ? {} : { registrationKind: "boss" }, protocol: INTERCOM_PROTOCOL_NAME, version: INTERCOM_PROTOCOL_VERSION, session, @@ -1267,15 +1793,21 @@ var IntercomClient = class extends EventEmitter2 { }); } handleBrokerMessage(msg) { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes3.isProxy(msg)) { throw new Error("Invalid broker message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if (typeDescriptor === void 0 || !typeDescriptor.enumerable || !Object.hasOwn(typeDescriptor, "value") || typeof typeDescriptor.value !== "string") throw new Error("Invalid broker message"); const brokerMessage = msg; if (this._sessionId === null && brokerMessage.type !== "registered" && brokerMessage.type !== "error") { throw new Error(`Received ${brokerMessage.type} before registered`); } switch (brokerMessage.type) { case "registered": { + parseExactRegisteredFrame( + brokerMessage, + this.requestedBossRegistration === void 0 ? this.remoteAccessCredential === void 0 ? "ordinary-local" : "ordinary-remote" : "boss" + ); if (typeof brokerMessage.sessionId !== "string" || brokerMessage.protocol !== INTERCOM_PROTOCOL_NAME || brokerMessage.version !== INTERCOM_PROTOCOL_VERSION) { throw new Error("Invalid registered message"); } @@ -1300,9 +1832,28 @@ var IntercomClient = class extends EventEmitter2 { } } } + if (this.requestedBossRegistration !== void 0) { + if (brokerMessage.remoteAccess !== void 0 || brokerMessage.access !== void 0) { + throw new Error("Boss registration returned folded remote-access metadata"); + } + const advertisement = parseBrokerCapabilityAdvertisement2(brokerMessage.capabilities); + if (!advertisement.features.some((feature) => feature.feature === BOSS_RUN_FEATURE2)) { + throw new Error("Broker did not echo the required boss-run-v1 feature contract"); + } + const binding = parseBossParticipantBindingMetadata(brokerMessage.boss, brokerMessage.sessionId); + const credential = this.requestedBossRegistration.credential; + if (binding.featureContract.feature !== this.requestedBossRegistration.featureContract.feature || binding.binding.bossRunId !== credential.bossRunId || binding.binding.participantId !== credential.participantId || binding.binding.role !== credential.role || binding.binding.communicationProfile !== credential.communicationProfile || binding.binding.bindingEpoch !== credential.bindingEpoch) { + throw new Error("Broker returned a Boss binding that does not match the authenticated registration request"); + } + this._bossBinding = binding; + } else if (brokerMessage.boss !== void 0) { + throw new Error("Broker attached unsolicited Boss binding metadata to an ordinary registration"); + } this._sessionId = brokerMessage.sessionId; this.outbox = new PersistentOutboundOutbox(brokerMessage.sessionId); + this.bossControlOutbox = this._bossBinding === void 0 ? null : new PersistentBossControlOutbox(brokerMessage.sessionId); this.replayOutbox(); + this.replayBossControlOutbox(); this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId }); break; } @@ -1327,6 +1878,48 @@ var IntercomClient = class extends EventEmitter2 { this.emit("message", from, message, deliveryId); break; } + case "boss_control": { + const { deliveryId, from } = brokerMessage; + if (typeof deliveryId !== "string" || !isSessionInfo(from)) { + throw new Error("Invalid boss_control event"); + } + const envelope = bossControlKind(brokerMessage.envelope).envelope; + const source = from.boss === void 0 ? void 0 : parseBossParticipantBindingMetadata(from.boss, from.id).binding; + if (source === void 0 || source.state !== "active" || source.bossRunId !== envelope.bossRunId || source.participantId !== envelope.participantId || source.bindingEpoch !== envelope.bindingEpoch) throw new Error("Boss control event sender does not match its broker-owned binding"); + this.emit("boss_control", from, envelope, deliveryId); + break; + } + case "boss_control_result": { + const result = parseBossControlResult(brokerMessage); + const { requestId, messageId, idempotencyKey, deliveryId } = result; + const stored = this.bossControlOutbox?.find(idempotencyKey); + if (!stored || stored.envelope.messageId !== requestId) throw new Error("Boss control result does not match the durable outbox binding"); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control result correlation does not match the pending request"); + } + this.bossControlOutbox.removeCorrelated(idempotencyKey, messageId, deliveryId); + if (pending) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(requestId); + pending.resolve(result); + } + break; + } + case "boss_control_ack": { + const { requestId, messageId, idempotencyKey, deliveryId } = parseBossControlAck(brokerMessage); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control acknowledgement correlation does not match the pending request"); + } + const transition = this.bossControlOutbox?.markAccepted(idempotencyKey, messageId, deliveryId); + if (transition === void 0) throw new Error("Boss control acknowledgement has no durable outbox"); + if (pending?.deliveryId !== void 0 && pending.deliveryId !== deliveryId) { + throw new Error("Boss control acknowledgement changed the pending deliveryId"); + } + if (pending) pending.deliveryId = deliveryId; + break; + } case "delivery_accepted": { const { deliveryId, messageId } = brokerMessage; if (typeof deliveryId !== "string" || typeof messageId !== "string") { @@ -1577,6 +2170,64 @@ var IntercomClient = class extends EventEmitter2 { } }); } + sendBossControl(to, envelopeValue) { + let socket; + try { + socket = this.requireActiveSocket(); + } catch (error) { + return Promise.reject(toError(error)); + } + let envelope; + try { + envelope = bossControlKind(envelopeValue).envelope; + const binding = this._bossBinding?.binding; + if (binding === void 0 || binding.state !== "active" || envelope.bossRunId !== binding.bossRunId || envelope.participantId !== binding.participantId || envelope.bindingEpoch !== binding.bindingEpoch) throw new Error("Boss control envelope does not match this client's active participant binding"); + } catch (error) { + return Promise.reject(toError(error)); + } + const requestId = envelope.messageId; + if (this.pendingBossControls.has(requestId)) { + return Promise.resolve({ + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "rejected", + delivered: false, + code: "INVALID_CONTROL", + reason: "Boss requestId is already pending" + }); + } + try { + if (!this.bossControlOutbox) throw new Error("Durable Boss control outbox is unavailable"); + this.bossControlOutbox.enqueue(to, envelope); + } catch (error) { + return Promise.reject(toError(error)); + } + return new Promise((resolve4, reject) => { + const timeout = setTimeout(() => { + if (!this.pendingBossControls.delete(requestId)) return; + reject(new Error("Boss control delivery timeout")); + }, 1e4); + timeout.unref?.(); + this.pendingBossControls.set(requestId, { + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + resolve: resolve4, + reject, + timeout + }); + try { + writeMessage(socket, { type: "boss_control", requestId, to, envelope }); + } catch (error) { + clearTimeout(timeout); + this.pendingBossControls.delete(requestId); + reject(toError(error)); + } + }); + } + acknowledgeBossControl(deliveryId, messageId, idempotencyKey) { + return this.writeControlMessage({ type: "boss_control_received", deliveryId, messageId, idempotencyKey }); + } acknowledgeMessage(deliveryId) { return this.writeControlMessage({ type: "message_received", deliveryId }); } @@ -1632,6 +2283,22 @@ var IntercomClient = class extends EventEmitter2 { } } } + replayBossControlOutbox() { + const socket = this.socket; + if (!socket || socket.destroyed || !this._sessionId || !this.bossControlOutbox) return; + for (const entry of this.bossControlOutbox.list()) { + try { + writeMessage(socket, { + type: "boss_control", + requestId: entry.envelope.messageId, + to: entry.to, + envelope: entry.envelope + }); + } catch { + return; + } + } + } updatePresence(updates) { if (this.disconnecting) { return; @@ -1646,38 +2313,39 @@ var IntercomClient = class extends EventEmitter2 { // broker/spawn.ts import { spawn as spawn2 } from "child_process"; -import { existsSync as existsSync3, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3 } from "fs"; -import { join as join4, dirname as dirname3 } from "path"; +import { existsSync as existsSync4, readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs"; +import { join as join5, dirname as dirname3 } from "path"; import { fileURLToPath } from "url"; import { createRequire } from "module"; import net3 from "net"; import { randomUUID as randomUUID3 } from "crypto"; +import { POLICY_SEMANTICS_HASH as POLICY_SEMANTICS_HASH2, POLICY_SEMANTICS_VERSION as POLICY_SEMANTICS_VERSION2 } from "@dataforxyz/agent-intercom-core"; var INTERCOM_DIR = getIntercomDirPath(); -var EXTENSION_DIR = join4(dirname3(fileURLToPath(import.meta.url)), ".."); -var BROKER_PID = join4(INTERCOM_DIR, "broker.pid"); -var BROKER_SPAWN_LOCK = join4(INTERCOM_DIR, "broker.spawn.lock"); +var EXTENSION_DIR = join5(dirname3(fileURLToPath(import.meta.url)), ".."); +var BROKER_PID = join5(INTERCOM_DIR, "broker.pid"); +var BROKER_SPAWN_LOCK = join5(INTERCOM_DIR, "broker.spawn.lock"); function sleep(ms) { return new Promise((resolve4) => setTimeout(resolve4, ms)); } function getBrokerEntryPath(moduleUrl = import.meta.url) { const moduleDir = dirname3(fileURLToPath(moduleUrl)); - const bundledBroker = join4(moduleDir, "broker.mjs"); - return existsSync3(bundledBroker) ? bundledBroker : join4(moduleDir, "broker.ts"); + const bundledBroker = join5(moduleDir, "broker.mjs"); + return existsSync4(bundledBroker) ? bundledBroker : join5(moduleDir, "broker.ts"); } function getTsxCliPath(extensionDir = EXTENSION_DIR) { try { const requireFromExtension = createRequire(import.meta.url); const tsxMain = requireFromExtension.resolve("tsx"); - return join4(dirname3(tsxMain), "cli.mjs"); + return join5(dirname3(tsxMain), "cli.mjs"); } catch { - return join4(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); + return join5(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); } } function quoteWindowsArg(value) { return `"${value.replace(/"/g, '""')}"`; } function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) { - return join4(intercomDir, "broker-launch.vbs"); + return join5(intercomDir, "broker-launch.vbs"); } function usesDefaultBrokerCommand(brokerCommand, brokerArgs) { return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx"; @@ -1705,7 +2373,7 @@ function isBrokerHealthOkMessage(message, requestId) { const remoteAccess = response.remoteAccess; if (typeof remoteAccess !== "object" || remoteAccess === null || Array.isArray(remoteAccess)) return false; const contract = remoteAccess; - return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION && contract.policySemanticsHash === POLICY_SEMANTICS_HASH; + return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION2 && contract.policySemanticsHash === POLICY_SEMANTICS_HASH2; } function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) { ensureIntercomRuntimeDir(dirname3(launcherPath)); @@ -1811,10 +2479,10 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs) { } } async function stopBrokerProcess(pidFile = BROKER_PID, timeoutMs = 3e3) { - if (!existsSync3(pidFile)) return; + if (!existsSync4(pidFile)) return; let pid; try { - pid = Number.parseInt(readFileSync5(pidFile, "utf-8").trim(), 10); + pid = Number.parseInt(readFileSync6(pidFile, "utf-8").trim(), 10); } catch { return; } @@ -1839,9 +2507,9 @@ async function isBrokerRunning() { if (await checkSocketConnectable()) { return true; } - if (!existsSync3(BROKER_PID)) return false; + if (!existsSync4(BROKER_PID)) return false; try { - const pid = parseInt(readFileSync5(BROKER_PID, "utf-8").trim(), 10); + const pid = parseInt(readFileSync6(BROKER_PID, "utf-8").trim(), 10); if (!Number.isFinite(pid)) return false; process.kill(pid, 0); return checkSocketConnectable(); @@ -1938,11 +2606,11 @@ ${Date.now()} return false; } function isSpawnLockStale() { - if (!existsSync3(BROKER_SPAWN_LOCK)) { + if (!existsSync4(BROKER_SPAWN_LOCK)) { return false; } try { - const [pidLine = "", createdAtLine = "0"] = readFileSync5(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); + const [pidLine = "", createdAtLine = "0"] = readFileSync6(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); const pid = Number.parseInt(pidLine, 10); const createdAt = Number.parseInt(createdAtLine, 10); const ageMs = Date.now() - createdAt; @@ -1976,8 +2644,8 @@ async function waitForBroker(timeoutMs = 5e3) { } // config.ts -import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs"; -import { join as join5, resolve as resolve3 } from "path"; +import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs"; +import { join as join6, resolve as resolve3 } from "path"; import { homedir as homedir2 } from "os"; var DEFAULT_ASK_TIMEOUT_MS = 45 * 1e3; var MAX_ASK_TIMEOUT_MS = 120 * 1e3; @@ -1991,8 +2659,8 @@ function validateAskTimeoutMs(value, name = "timeout_ms") { return value; } function getConfigPath() { - const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve3(process.env.PI_CODING_AGENT_DIR) : join5(homedir2(), ".pi", "agent"); - return join5(agentDir, "intercom", "config.json"); + const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve3(process.env.PI_CODING_AGENT_DIR) : join6(homedir2(), ".pi", "agent"); + return join6(agentDir, "intercom", "config.json"); } var defaults = { brokerCommand: "npx", @@ -2009,11 +2677,11 @@ var defaults = { }; function loadConfig() { const configPath = getConfigPath(); - if (!existsSync4(configPath)) { + if (!existsSync5(configPath)) { return { ...defaults }; } try { - const raw = readFileSync6(configPath, "utf-8"); + const raw = readFileSync7(configPath, "utf-8"); const parsed = JSON.parse(raw); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error("Config must be a JSON object"); @@ -2137,39 +2805,126 @@ async function resolveContactTarget(id, name, listSessions) { // codex/team.ts import { readFile } from "node:fs/promises"; -import { join as join6 } from "node:path"; -var LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +import { join as join7 } from "node:path"; +import { + BOSS_PARTICIPANT_ROLES, + parseParticipantState as parseParticipantState2, + parseWorkerIdentityV2 as parseWorkerIdentityV22, + workerIdentityFromEnvironment +} from "@dataforxyz/agent-intercom-core/boss"; +var LEGACY_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +var CANONICAL_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "registering", "ready", "working", "waiting", "paused", "stalled", "blocked", "unreachable"]); var stringValue = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0; var connectedTo = (sessions, target) => { const normalized = target.toLowerCase(); return sessions.some((session) => session.id === target || session.name?.toLowerCase() === normalized); }; +function bossIdentityFromEnvironment(env) { + const bossKeys = ["AGENT_INTERCOM_BOSS_RUN_ID", "AGENT_INTERCOM_PARTICIPANT_ID", "AGENT_INTERCOM_BINDING_EPOCH"]; + if (!bossKeys.some((key) => env[key] !== void 0)) return void 0; + const identity = workerIdentityFromEnvironment(env); + if (!("bossRunId" in identity)) throw new Error("Incomplete Boss worker identity cannot discover a team"); + return identity; +} +function canonicalWorker(value) { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("worker must be an object"); + const worker = value; + const identity = parseWorkerIdentityV22({ + version: "orc.worker-identity.v2", + workerId: worker.id, + workerIncarnationId: worker.workerIncarnationId, + workerGeneration: worker.workerGeneration, + ...worker.bossRunId === void 0 ? {} : { bossRunId: worker.bossRunId }, + ...worker.participantId === void 0 ? {} : { participantId: worker.participantId }, + ...worker.bindingEpoch === void 0 ? {} : { bindingEpoch: worker.bindingEpoch } + }); + parseParticipantState2(worker.state, "$.worker.state"); + if (typeof worker.role !== "string" || !BOSS_PARTICIPANT_ROLES.includes(worker.role)) { + throw new Error("worker role is not canonical"); + } + if (worker.owned !== true || !stringValue(worker.managerSessionId) || !stringValue(worker.intercomTarget)) { + throw new Error("canonical worker ownership routing is incomplete"); + } + return { ...worker, canonicalIdentity: identity }; +} +function exactBossRosterSession(sessions, worker) { + const identity = worker.canonicalIdentity; + const target = stringValue(worker.intercomTarget); + const role = stringValue(worker.role); + const state = stringValue(worker.state); + if (!identity || !("bossRunId" in identity) || !target || !role || !state) return void 0; + const matches = sessions.filter((candidate) => candidate.id === target); + if (matches.length !== 1) return void 0; + const [session] = matches; + if (!session?.boss?.binding || session.boss.workerIdentity === void 0 || session.boss.participantState === void 0) return void 0; + try { + const sessionIdentity = parseWorkerIdentityV22(session.boss.workerIdentity); + const sessionState = parseParticipantState2(session.boss.participantState, "$.session.boss.participantState"); + const binding = session.boss.binding; + return "bossRunId" in sessionIdentity && session.id === target && binding.sessionId === session.id && binding.state === "active" && binding.bossRunId === identity.bossRunId && binding.participantId === identity.participantId && binding.bindingEpoch === identity.bindingEpoch && binding.role === role && sessionIdentity.workerId === identity.workerId && sessionIdentity.workerIncarnationId === identity.workerIncarnationId && sessionIdentity.workerGeneration === identity.workerGeneration && sessionIdentity.bossRunId === identity.bossRunId && sessionIdentity.participantId === identity.participantId && sessionIdentity.bindingEpoch === identity.bindingEpoch && sessionState === state ? session : void 0; + } catch { + return void 0; + } +} async function readWorkers(agentDir) { try { - const parsed = JSON.parse(await readFile(join6(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); - return Array.isArray(parsed.workers) ? parsed.workers : []; + const parsed = JSON.parse(await readFile(join7(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("worker snapshot must be an object"); + const snapshot = parsed; + if (snapshot.version !== 1 && snapshot.version !== 2 || !Array.isArray(snapshot.workers)) throw new Error("unsupported worker snapshot version"); + if (snapshot.version === 1) return { version: 1, workers: snapshot.workers }; + return { version: 2, workers: snapshot.workers.map(canonicalWorker) }; } catch { - return []; + return { version: 1, workers: [] }; } } async function resolveIntercomTeam(input) { const env = input.env ?? process.env; - const workers = await readWorkers(input.agentDir ?? getAgentDirPath()); + const snapshot = await readWorkers(input.agentDir ?? getAgentDirPath()); + const workers = snapshot.workers; const workerId = stringValue(env.AGENT_INTERCOM_WORKER_ID); + const bossIdentity = bossIdentityFromEnvironment(env); const runId = stringValue(env.AGENT_INTERCOM_RUN_ID); - const current = workerId ? workers.find((worker) => stringValue(worker.id) === workerId && (!runId || stringValue(worker.runId) === runId)) : void 0; - const managerTarget = stringValue(current?.managerSessionId) ?? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID); + const currentMatches = workerId ? workers.filter((worker) => stringValue(worker.id) === workerId && (bossIdentity === void 0 ? !runId || stringValue(worker.runId) === runId : snapshot.version === 2 && worker.canonicalIdentity?.workerId === bossIdentity.workerId && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId && worker.canonicalIdentity.participantId === bossIdentity.participantId && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch)) : []; + const current = bossIdentity === void 0 ? currentMatches[0] : currentMatches.length === 1 ? currentMatches[0] : void 0; + const currentTarget = stringValue(current?.intercomTarget); + const exactCurrentProjection = current !== void 0 && currentTarget === input.selfId && workers.filter((worker) => stringValue(worker.id) === workerId).length === 1 && workers.filter((worker) => stringValue(worker.intercomTarget) === currentTarget).length === 1 && exactBossRosterSession(input.sessions, current) !== void 0; + if (bossIdentity !== void 0 && !exactCurrentProjection) { + return { self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false }, coworkers: [] }; + } + const managerTarget = stringValue(current?.managerSessionId) ?? (bossIdentity === void 0 ? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID) : void 0); const teamId = managerTarget ?? input.selfId; - const coworkers = workers.filter((worker) => worker.owned === true).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { + const currentRole = stringValue(current?.role); + const canDiscoverOwnedRoster = bossIdentity === void 0 || currentRole === "manager" || currentRole === "controller"; + const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true).filter((worker) => bossIdentity === void 0 || snapshot.version === 2 && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => stringValue(worker.intercomTarget) !== managerTarget).filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { const id = stringValue(worker.id); if (!id) return void 0; const target = stringValue(worker.intercomTarget) ?? id; - return { id, target, ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, connected: connectedTo(input.sessions, target) }; + const connected = bossIdentity === void 0 ? connectedTo(input.sessions, target) : exactBossRosterSession(input.sessions, worker) !== void 0; + if (!connected) return void 0; + return { + id, + target, + ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, + ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, + ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, + connected + }; }).filter((member) => Boolean(member)); - return { teamId, self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: !managerTarget }, manager: managerTarget ? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } : { target: input.selfId, connected: true }, coworkers }; + const managerWorker = managerTarget === void 0 ? void 0 : workers.find((worker) => stringValue(worker.intercomTarget) === managerTarget && (bossIdentity === void 0 || snapshot.version === 2 && stringValue(worker.role) === "manager" && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId)); + const managerConnected = managerTarget === void 0 ? true : bossIdentity === void 0 ? connectedTo(input.sessions, managerTarget) : managerWorker !== void 0 && exactBossRosterSession(input.sessions, managerWorker) !== void 0; + return { + teamId, + self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: bossIdentity === void 0 && !managerTarget }, + ...managerTarget ? { manager: { target: managerTarget, connected: managerConnected } } : bossIdentity === void 0 ? { manager: { target: input.selfId, connected: true } } : {}, + coworkers + }; } function formatIntercomTeam(team) { - const lines = [`Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}`]; + const lines = [ + `Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, + `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}` + ]; if (!team.coworkers.length) lines.push("Coworkers: none"); else { lines.push("Coworkers:"); @@ -2313,6 +3068,25 @@ function threadSandboxMode(sandboxPolicy) { return "read-only"; } } +function protectedBossClientForBridge(config) { + assertBossCanonicalData(config, "$.bridgeConfig"); + if (!Array.isArray(config.agents)) return void 0; + for (const agent of config.agents) { + if (typeof agent !== "object" || agent === null || Array.isArray(agent)) continue; + if (agent.bossClient === "boss_participant" || agent.bossClient === "boss_reviewer") return agent.bossClient; + } + return void 0; +} +function bridgeAgentSandboxMode(agent) { + return agent.sandboxPolicy === void 0 ? bridgeAgentDefaultSandbox(agent) ?? "read-only" : threadSandboxMode(agent.sandboxPolicy); +} +function bridgeAgentTurnSandboxPolicy(agent) { + if (agent.sandboxPolicy !== void 0) return agent.sandboxPolicy; + if (bridgeAgentSandboxMode(agent) === "workspace-write") { + throw new Error("workspace-write requires unavailable broker-owned assigned workspace authority"); + } + return { type: "readOnly", networkAccess: false }; +} function getTurnId(result) { const turn = result && typeof result === "object" ? result.turn : void 0; if (!turn || typeof turn !== "object" || typeof turn.id !== "string") { @@ -2596,12 +3370,12 @@ var VirtualCodexAgent = class { async ensureThread() { if (this.threadId) { try { - const sandbox2 = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox2 = bridgeAgentSandboxMode(this.agent); await this.app.request("thread/resume", { threadId: this.threadId, cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox: sandbox2 }); return this.threadId; @@ -2609,11 +3383,11 @@ var VirtualCodexAgent = class { this.threadId = null; } } - const sandbox = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox = bridgeAgentSandboxMode(this.agent); const result = await this.app.request("thread/start", { cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox, serviceName: "codex-intercom", developerInstructions: this.agent.instructions ?? null, @@ -2664,8 +3438,8 @@ var VirtualCodexAgent = class { threadId, input, cwd: this.agent.cwd, - approvalPolicy: this.agent.approvalPolicy ?? "never", - sandboxPolicy: this.agent.sandboxPolicy ?? { type: "readOnly", networkAccess: false }, + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), + sandboxPolicy: bridgeAgentTurnSandboxPolicy(this.agent), model: this.agent.model ?? null }); } @@ -2880,7 +3654,11 @@ var CodexBridgeDaemon = class { constructor(config, hooks = {}) { this.config = config; this.hooks = hooks; - this.app = new CodexAppServerClient(config.appServer); + const protectedBossClient = protectedBossClientForBridge(config); + assertHardenedBossProviderAuthority(protectedBossClient); + assertHardenedBossBridgeConfig(config); + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); + this.app = new CodexAppServerClient(config.appServer, protectedBossClient); this.app.setServerRequestHandler((message) => this.handleServerRequest(message)); } config; @@ -2889,6 +3667,9 @@ var CodexBridgeDaemon = class { agents = []; inflightToolCalls = /* @__PURE__ */ new Map(); async start() { + assertHardenedBossProviderAuthority(protectedBossClientForBridge(this.config)); + assertHardenedBossBridgeConfig(this.config); + for (const agent of this.config.agents) assertHardenedBossAgentConfig(agent); const intercomConfig = loadConfig(); await spawnBrokerIfNeeded(intercomConfig.brokerCommand, intercomConfig.brokerArgs); await this.app.connect(); diff --git a/dist/broker.mjs b/dist/broker.mjs index 0e78392..9299828 100644 --- a/dist/broker.mjs +++ b/dist/broker.mjs @@ -1,216 +1,13 @@ -process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=broker sourceSha256=28cbe04c291ec9ca89e519b437e41d7a7c359f85cf99d2fcf3e6cb0f74dcee2c\n"); +process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=broker sourceSha256=e3924d8a81ca3579d920e6938f77fe75f36c8eeb02d45b49b2d940b7a67b6410\n"); // broker/broker.ts import net from "net"; -import { existsSync as existsSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs"; +import { existsSync as existsSync3, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs"; import { join as join2 } from "path"; import { randomUUID as randomUUID3 } from "crypto"; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy.js -var POLICY_SEMANTICS_VERSION = 2; -function activePrincipal(state, id) { - return state.principals[id]; -} -function isDirectParentPair(left, right) { - return left.parentSessionId === right.id || right.parentSessionId === left.id; -} -function isAncestor(state, ancestorId, descendantId) { - if (ancestorId === descendantId) - return false; - const visited = /* @__PURE__ */ new Set(); - let current = state.principals[descendantId]; - while (current?.parentSessionId && !visited.has(current.id)) { - if (current.parentSessionId === ancestorId) - return true; - visited.add(current.id); - current = state.principals[current.parentSessionId]; - } - return false; -} -function authorize(state, actorId, action, targetId, context = {}) { - const actor = activePrincipal(state, actorId); - const target = activePrincipal(state, targetId); - if (!actor || !target) - return { allowed: false, code: "UNKNOWN_PRINCIPAL" }; - if (actor.state !== "active" || target.state !== "active") - return { allowed: false, code: "REVOKED_PRINCIPAL" }; - if (context.actorGeneration !== void 0 && context.actorGeneration !== actor.generation || context.targetGeneration !== void 0 && context.targetGeneration !== target.generation) { - return { allowed: false, code: "STALE_GENERATION" }; - } - if (actor.id === target.id) - return { allowed: true, reason: "self" }; - if (actor.kind === "local" && target.kind === "local") - return { allowed: true, reason: "local-public" }; - if (action === "discover" || action === "send" || action === "ask" || action === "reply") { - if (isDirectParentPair(actor, target)) - return { allowed: true, reason: "direct-parent" }; - if (isAncestor(state, actor.id, target.id) || isAncestor(state, target.id, actor.id)) { - return { allowed: true, reason: "ancestor-chain" }; - } - } - if (action === "inspect_tree" || action === "revoke" || action === "adopt") { - if (isAncestor(state, actor.id, target.id)) - return { allowed: true, reason: "ancestor-control" }; - } - return { allowed: false, code: "POLICY_DENIED" }; -} - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy-vectors.js -var localRoot = { - id: "local-root", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-root" -}; -var localPeer = { - id: "local-peer", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-peer" -}; -var remoteManager = { - id: "remote-manager", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "local-root", - rootSessionId: "local-root" -}; -var remoteChild = { - id: "remote-child", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var remoteSibling = { - id: "remote-sibling", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var POLICY_VECTORS = [ - { - name: "local sessions remain public", - principals: [localRoot, localPeer], - actorId: "local-root", - action: "send", - targetId: "local-peer", - expectedAllowed: true, - expectedReasonOrCode: "local-public" - }, - { - name: "remote manager can reach direct local parent", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "local parent can reach direct remote child", - principals: [localRoot, remoteManager], - actorId: "local-root", - action: "ask", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "remote child can reach its local root through the ancestor chain", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-chain" - }, - { - name: "remote siblings cannot communicate in phase one", - principals: [localRoot, remoteManager, remoteChild, remoteSibling], - actorId: "remote-child", - action: "discover", - targetId: "remote-sibling", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "unrelated local session cannot discover remote principal", - principals: [localRoot, localPeer, remoteManager], - actorId: "local-peer", - action: "discover", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal cannot reach unrelated local session", - principals: [localRoot, localPeer, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-peer", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote manager may inspect its descendant subtree", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-manager", - action: "inspect_tree", - targetId: "remote-child", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-control" - }, - { - name: "remote child cannot revoke its ancestor", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "revoke", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal may request attenuated delegation under itself", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "delegate_child", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "self" - }, - { - name: "revoked principal cannot communicate", - principals: [localRoot, { ...remoteManager, state: "revoked" }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: false, - expectedReasonOrCode: "REVOKED_PRINCIPAL" - }, - { - name: "stale actor generation cannot send", - principals: [localRoot, { ...remoteManager, generation: 2 }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - context: { actorGeneration: 1 }, - expectedAllowed: false, - expectedReasonOrCode: "STALE_GENERATION" - } -]; -var POLICY_SEMANTICS_HASH = "f3b00e503631bc91123aedfbcf1df72cc9913e1893c09728b2c598f3dcdfdfe0"; +import { types as nodeUtilTypes2 } from "node:util"; +import { authorize, POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION } from "@dataforxyz/agent-intercom-core"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; // broker/framing.ts var MAX_FRAME_BYTES = 1024 * 1024; @@ -800,45 +597,379 @@ var RemoteAccessRegistry = class { } }; -// broker/authorization.ts -function policyPrincipalForSession(session) { - if (session.origin === "remote") { - if (!session.parentSessionId || !session.rootSessionId || !session.generation) { - throw new Error(`Remote session ${session.id} is missing broker-owned policy metadata`); +// broker/boss-adapter.ts +import { + BOSS_CAPABILITY_FEATURE_DIGEST, + BOSS_CONTROL_ENVELOPE_VERSION, + BOSS_POLICY_PRINCIPAL_VERSION, + BOSS_POLICY_SEMANTICS_HASH, + BOSS_RUN_FEATURE, + BOSS_RUN_FEATURE_CONTRACT, + BOSS_RUN_FEATURE_SEMANTICS_HASH, + BOSS_RUN_FEATURE_VERSION, + BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + BROKER_FEATURE_ATTESTATION_VERSION, + INTERCOM_BASE_PROTOCOL_VERSION, + authorizeFeatureAware, + brokerFeatureSetHash, + parseBossControlEnvelope, + parseBossParticipantBinding, + parseBossParticipantCredentialEnvelope, + parseBossRunFeatureContract, + parseBrokerCapabilityAdvertisement, + parseParticipantState, + parseWorkerIdentityV2 +} from "@dataforxyz/agent-intercom-core/boss"; +import { + ContractValidationError, + assertExactKeys, + assertRecord, + canonicalJson +} from "@dataforxyz/agent-intercom-core/canonical"; +import { types as nodeUtilTypes } from "node:util"; +var BOSS_ADVERTISEMENT_PREDICATES = [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth" +]; +var DORMANT_BOSS_ADVERTISEMENT_READINESS = Object.freeze({ + protectedProvider: false, + brokerIdentity: false, + credentialRegistry: false, + authorityTransitions: false, + participantHealth: false +}); +var ORDINARY_SESSION_REGISTRATION_KEYS = [ + "cwd", + "model", + "pid", + "startedAt", + "lastActivity" +]; +var OPTIONAL_SESSION_REGISTRATION_KEYS = ["name", "status", "runtimeInstanceId"]; +function assertBossCanonicalData(value, path = "$", seen = /* @__PURE__ */ new WeakSet()) { + if (typeof value !== "object" || value === null) return; + if (nodeUtilTypes.isProxy(value)) { + throw new ContractValidationError(path, "proxies are not supported"); + } + if (seen.has(value)) throw new ContractValidationError(path, "cyclic values are not supported"); + seen.add(value); + if (Array.isArray(value)) { + if (Object.getPrototypeOf(value) !== Array.prototype) { + throw new ContractValidationError(path, "must use the exact Array prototype"); + } + const ownKeys = Reflect.ownKeys(value); + const expectedKeys = /* @__PURE__ */ new Set(["length"]); + for (let index = 0; index < value.length; index += 1) expectedKeys.add(String(index)); + if (ownKeys.length !== expectedKeys.size || ownKeys.some((key) => !expectedKeys.has(key))) { + throw new ContractValidationError(path, "must be a dense array without symbols or extra properties"); + } + for (let index = 0; index < value.length; index += 1) { + if (!Object.hasOwn(value, index)) { + throw new ContractValidationError(`${path}[${index}]`, "sparse array holes are not supported"); + } + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}[${index}]`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}[${index}]`, seen); } - return { - id: session.id, - kind: "remote", - state: "active", - generation: session.generation, - policy: "remote-tree", - parentSessionId: session.parentSessionId, - rootSessionId: session.rootSessionId - }; + return; + } + if (Object.getPrototypeOf(value) !== Object.prototype) { + throw new ContractValidationError(path, "must use the exact Object prototype"); + } + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") throw new ContractValidationError(path, "symbol properties are not supported"); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}.${key}`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}.${key}`, seen); + } +} +function parseBossAdvertisementReadiness(value) { + assertBossCanonicalData(value, "$.readiness"); + assertRecord(value); + assertExactKeys(value, BOSS_ADVERTISEMENT_PREDICATES); + const parsed = {}; + for (const predicate of BOSS_ADVERTISEMENT_PREDICATES) { + const enabled = ownDataValue(value, predicate); + if (typeof enabled !== "boolean") { + throw new ContractValidationError(`$.readiness.${predicate}`, "must be a boolean"); + } + parsed[predicate] = enabled; + } + return parsed; +} +function missingBossAdvertisementPredicates(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + const parsed = parseBossAdvertisementReadiness(readiness); + return BOSS_ADVERTISEMENT_PREDICATES.filter((predicate) => parsed[predicate] !== true); +} +function bossCapabilityAdvertisement(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + if (missingBossAdvertisementPredicates(readiness).length > 0) return void 0; + const features = [{ + version: BROKER_FEATURE_ATTESTATION_VERSION, + feature: BOSS_RUN_FEATURE, + featureVersion: BOSS_RUN_FEATURE_VERSION, + semanticsHash: BOSS_RUN_FEATURE_SEMANTICS_HASH, + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }]; + return parseBrokerCapabilityAdvertisement({ + baseProtocolVersion: INTERCOM_BASE_PROTOCOL_VERSION, + features, + protocolFeatureContractHash: BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + featureSetHash: brokerFeatureSetHash(features), + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }); +} +function optionalOwnDataValue(value, key) { + assertRecord(value); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0) return void 0; + if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`$.${key}`, "must be an own enumerable data property"); + } + return descriptor.value; +} +function ownDataValue(value, key) { + const result = optionalOwnDataValue(value, key); + if (result === void 0) throw new ContractValidationError(`$.${key}`, "is required"); + return result; +} +function parseBossParticipantRegistrationMetadata(value) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys(value, ["featureContract", "credential"]); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly negotiate boss-run-v1 over base protocol v3"); + } + const credential = parseBossParticipantCredentialEnvelope(ownDataValue(value, "credential")); + if (credential.namespace !== featureContract.feature) { + throw new ContractValidationError("$.credential.namespace", "must match the negotiated feature namespace"); + } + return { featureContract, credential }; +} +function exactRegistrationKind(session, value) { + assertBossCanonicalData(session, "$.session"); + assertRecord(session); + const boss = optionalOwnDataValue(session, "boss"); + if (boss === void 0) { + if (value === void 0) return "ordinary"; + throw new ContractValidationError("$.registrationKind", "must be absent when Boss metadata is absent"); + } + if (value !== "boss") { + throw new ContractValidationError("$.registrationKind", "must be boss when Boss metadata is present"); + } + return "boss"; +} +function parseExactRegistrationFrame(value) { + assertBossCanonicalData(value, "$.register"); + assertRecord(value); + const session = ownDataValue(value, "session"); + assertRecord(session); + const registrationKind = optionalOwnDataValue(value, "registrationKind"); + const kind = exactRegistrationKind(session, registrationKind); + if (kind === "ordinary") { + assertExactKeys(value, ["type", "protocol", "version", "session"], ["sessionId", "stateId", "access"]); + assertExactKeys(session, ORDINARY_SESSION_REGISTRATION_KEYS, OPTIONAL_SESSION_REGISTRATION_KEYS); + } else { + assertExactKeys(value, ["type", "registrationKind", "protocol", "version", "session"], ["sessionId", "stateId"]); + assertExactKeys(session, [...ORDINARY_SESSION_REGISTRATION_KEYS, "boss"], OPTIONAL_SESSION_REGISTRATION_KEYS); + parseBossParticipantRegistrationMetadata(ownDataValue(session, "boss")); + } + if (ownDataValue(value, "type") !== "register") { + throw new ContractValidationError("$.register.type", "must be register"); } + return value; +} +function parseBossParticipantBindingMetadata(value, expectedSessionId) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys( + value, + ["featureContract", "binding", "brokerIdentityVerified"], + ["assignedParticipantIds", "requestingPrincipalId", "workerIdentity", "participantState"] + ); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly bind boss-run-v1 over base protocol v3"); + } + const binding = parseBossParticipantBinding(ownDataValue(value, "binding")); + if (ownDataValue(value, "brokerIdentityVerified") !== true) { + throw new ContractValidationError("$.brokerIdentityVerified", "must be true for a broker-owned Boss binding"); + } + if (expectedSessionId !== void 0 && binding.sessionId !== expectedSessionId) { + throw new ContractValidationError("$.binding.sessionId", "must match the registered intercom session"); + } + const rawAssignedParticipantIds = optionalOwnDataValue(value, "assignedParticipantIds"); + let assignedParticipantIds; + if (rawAssignedParticipantIds !== void 0) { + if (binding.role !== "manager" || !Array.isArray(rawAssignedParticipantIds) || rawAssignedParticipantIds.some((entry) => typeof entry !== "string" || entry.length === 0) || new Set(rawAssignedParticipantIds).size !== rawAssignedParticipantIds.length) { + throw new ContractValidationError("$.assignedParticipantIds", "must be a unique participant list present only for a Manager"); + } + assignedParticipantIds = rawAssignedParticipantIds; + } + if (binding.role === "manager" && assignedParticipantIds === void 0) { + throw new ContractValidationError("$.assignedParticipantIds", "is required for a Manager policy binding"); + } + const rawRequestingPrincipalId = optionalOwnDataValue(value, "requestingPrincipalId"); + if (binding.role === "council" !== (typeof rawRequestingPrincipalId === "string" && rawRequestingPrincipalId.length > 0)) { + throw new ContractValidationError("$.requestingPrincipalId", "is required exactly for a Council policy binding"); + } + const requestingPrincipalId = typeof rawRequestingPrincipalId === "string" ? rawRequestingPrincipalId : void 0; + const rawWorkerIdentity = optionalOwnDataValue(value, "workerIdentity"); + const rawParticipantState = optionalOwnDataValue(value, "participantState"); + if (rawWorkerIdentity === void 0 !== (rawParticipantState === void 0)) { + throw new ContractValidationError("$.workerIdentity", "workerIdentity and participantState must be supplied together"); + } + const workerIdentity = rawWorkerIdentity === void 0 ? void 0 : parseWorkerIdentityV2(rawWorkerIdentity); + const participantState = rawParticipantState === void 0 ? void 0 : parseParticipantState(rawParticipantState, "$.participantState"); + if (workerIdentity !== void 0 && (!("bossRunId" in workerIdentity) || workerIdentity.bossRunId !== binding.bossRunId || workerIdentity.participantId !== binding.participantId || workerIdentity.bindingEpoch !== binding.bindingEpoch)) throw new ContractValidationError("$.workerIdentity", "must match the broker-owned participant binding"); return { - id: session.id, - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: session.id + featureContract, + binding, + brokerIdentityVerified: true, + ...assignedParticipantIds === void 0 ? {} : { assignedParticipantIds: [...assignedParticipantIds] }, + ...requestingPrincipalId === void 0 ? {} : { requestingPrincipalId }, + ...workerIdentity === void 0 ? {} : { workerIdentity, participantState } }; } -function policyStateForSessions(sessions) { - const principals = {}; - for (const session of sessions) principals[session.id] = policyPrincipalForSession(session); - return { principals }; +function bossPrincipal(session, metadata) { + const { binding } = metadata; + return { + version: BOSS_POLICY_PRINCIPAL_VERSION, + principalId: session.id, + principalClass: "boss-private", + state: binding.state, + bossRunId: binding.bossRunId, + participantId: binding.participantId, + role: binding.role, + bindingEpoch: binding.bindingEpoch, + ...binding.assignedManagerParticipantId === void 0 ? {} : { assignedManagerParticipantId: binding.assignedManagerParticipantId }, + ...metadata.assignedParticipantIds === void 0 ? {} : { assignedParticipantIds: metadata.assignedParticipantIds }, + ...metadata.requestingPrincipalId === void 0 ? {} : { requestingPrincipalId: metadata.requestingPrincipalId } + }; +} +function featurePolicyStateForSessions(sessions) { + const legacy = { principals: {} }; + const boss = { principals: {} }; + const registrations = {}; + for (const session of sessions) { + if (session.boss !== void 0) { + const metadata = parseBossParticipantBindingMetadata(session.boss, session.id); + boss.principals[session.id] = bossPrincipal(session, metadata); + registrations[session.id] = { + principalId: session.id, + principalClass: "boss-bound", + state: metadata.binding.state, + bossRunId: metadata.binding.bossRunId, + participantId: metadata.binding.participantId, + bindingEpoch: metadata.binding.bindingEpoch, + featureContract: metadata.featureContract, + policySemanticsHash: BOSS_POLICY_SEMANTICS_HASH, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST, + brokerIdentityVerified: metadata.brokerIdentityVerified + }; + continue; + } + const principal = session.origin === "remote" ? (() => { + if (!session.parentSessionId || !session.rootSessionId || !session.generation) { + throw new Error(`Remote session ${session.id} is missing broker-owned policy metadata`); + } + return { + id: session.id, + kind: "remote", + state: "active", + generation: session.generation, + policy: "remote-tree", + parentSessionId: session.parentSessionId, + rootSessionId: session.rootSessionId + }; + })() : { + id: session.id, + kind: "local", + state: "active", + generation: 1, + policy: "local-public", + rootSessionId: session.id + }; + legacy.principals[session.id] = principal; + registrations[session.id] = { principalId: session.id, principalClass: "ordinary", state: "active" }; + } + return { legacy, boss, registrations }; } -function authorizeSessionAction(sessions, actorId, action, targetId) { - const state = policyStateForSessions(sessions); - const actor = state.principals[actorId]; - const target = state.principals[targetId]; - return authorize(state, actorId, action, targetId, { - actorGeneration: actor?.generation, - targetGeneration: target?.generation +function authorizeBossAwareSessionAction(sessions, actorId, action, targetId, bossContext) { + const values = Array.from(sessions); + const state = featurePolicyStateForSessions(values); + const actor = state.registrations[actorId]; + const target = state.registrations[targetId]; + return authorizeFeatureAware(state, { + actorId, + action, + targetId, + ...actor?.principalClass === "ordinary" && target?.principalClass === "ordinary" ? { + legacyContext: { + actorGeneration: state.legacy.principals[actorId]?.generation, + targetGeneration: state.legacy.principals[targetId]?.generation + } + } : bossContext === void 0 ? {} : { bossContext } }); } +var BOSS_CONTROL_KIND_BY_TYPE = { + "boss.assignment.created": "assignment_request", + "boss.assignment.accepted": "assignment_response", + "boss.assignment.checkpoint": "assignment_response", + "boss.assignment.submitted": "assignment_response", + "boss.assignment.rejected": "assignment_response", + "boss.assignment.cancelled": "lifecycle", + "boss.staffing.requested": "staffing", + "boss.staffing.resolved": "staffing", + "boss.review.requested": "review_request", + "boss.review.submitted": "review_result", + "boss.council.requested": "review_request", + "boss.council.submitted": "review_result", + "boss.proof.submitted": "proof", + "boss.worker.health": "health", + "boss.worker.blocked": "health", + "boss.worker.failed": "health", + "boss.worker.notice": "lifecycle", + "boss.worker.notice_delivery_failed": "lifecycle", + "boss.decision.required": "decision" +}; +function bossControlKind(envelopeValue) { + assertBossCanonicalData(envelopeValue); + const envelope = parseBossControlEnvelope(envelopeValue); + return { envelope, controlKind: BOSS_CONTROL_KIND_BY_TYPE[envelope.type] }; +} +function hasAuthoritativeBossControlCorrelation() { + return false; +} +function exactBossSessionTarget(sessions, requestedSessionId) { + const target = sessions.get(requestedSessionId); + return target?.info.id === requestedSessionId ? target : void 0; +} +function assertBossControlSender(session, envelopeValue) { + const { envelope } = bossControlKind(envelopeValue); + if (session.boss === void 0) { + throw new ContractValidationError("$.session", "ordinary sessions cannot originate Boss control envelopes"); + } + const { binding } = parseBossParticipantBindingMetadata(session.boss, session.id); + if (binding.state !== "active" || envelope.bossRunId !== binding.bossRunId || envelope.participantId !== binding.participantId || envelope.bindingEpoch !== binding.bindingEpoch) { + throw new ContractValidationError("$.envelope", "does not match the active broker-owned participant binding"); + } + return envelope; +} + +// broker/authorization.ts +function authorizeSessionAction(sessions, actorId, action, targetId, bossContext) { + return authorizeBossAwareSessionAction(sessions, actorId, action, targetId, bossContext); +} function visibleSessions(sessions, actorId) { const values = Array.from(sessions); return values.filter((target) => authorizeSessionAction(values, actorId, "discover", target.id).allowed); @@ -879,6 +1010,196 @@ var BrokerAuditLog = class { } }; +// broker/boss-control-ledger.ts +import { existsSync as existsSync2, readFileSync as readFileSync4, renameSync as renameSync2 } from "node:fs"; +import { canonicalJson as canonicalJson2 } from "@dataforxyz/agent-intercom-core/canonical"; +var BOSS_CONTROL_LEDGER_VERSION = 3; +var EXPIRING_BOSS_CONTROL_LEDGER_VERSION = 2; +var MAX_BOSS_CONTROL_RESULTS = 2048; +var BOSS_CONTROL_FAILURE_CODES = /* @__PURE__ */ new Set([ + "INVALID_CONTROL", + "IDEMPOTENCY_CONFLICT", + "SESSION_NOT_FOUND", + "POLICY_DENIED", + "RECIPIENT_DISCONNECTED", + "DELIVERY_TIMEOUT" +]); +function exactStringKeys(value, required, optional = []) { + const keys = Reflect.ownKeys(value); + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseBossControlResult(value) { + assertBossCanonicalData(value, "$.bossControlResult"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control result must be an exact plain object"); + } + const result = value; + const base = typeof result.requestId === "string" && result.requestId.length > 0 && result.messageId === result.requestId && typeof result.idempotencyKey === "string" && result.idempotencyKey.length > 0; + if (!base || result.type !== "boss_control_result") throw new Error("Invalid Boss control result binding"); + if (result.status === "delivered" && result.delivered === true && typeof result.deliveryId === "string" && result.deliveryId.length > 0 && exactStringKeys(result, ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "deliveryId"])) return result; + if (result.status === "rejected" && result.delivered === false && typeof result.code === "string" && BOSS_CONTROL_FAILURE_CODES.has(result.code) && typeof result.reason === "string" && result.reason.length > 0 && (!Object.hasOwn(result, "deliveryId") || typeof result.deliveryId === "string" && result.deliveryId.length > 0) && exactStringKeys( + result, + ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "code", "reason"], + ["deliveryId"] + )) return result; + throw new Error("Invalid Boss control result discriminant"); +} +function rebindBossControlResult(resultValue, messageId) { + if (typeof messageId !== "string" || messageId.length === 0) throw new Error("Replay messageId is required"); + const result = parseBossControlResult(resultValue); + return parseBossControlResult({ ...result, requestId: messageId, messageId }); +} +function bossControlReplayFrames(resultValue, messageId) { + const result = rebindBossControlResult(resultValue, messageId); + if (result.deliveryId === void 0) return [result]; + return [{ + type: "boss_control_ack", + requestId: messageId, + messageId, + idempotencyKey: result.idempotencyKey, + status: "accepted", + deliveryId: result.deliveryId + }, result]; +} +function bossControlAcceptedRecoveryFrames(resultValue) { + const result = parseBossControlResult(resultValue); + if (result.status !== "rejected" || result.deliveryId === void 0) { + throw new Error("Accepted Boss recovery requires a delivery-bound rejected result"); + } + return [{ + type: "boss_control_ack", + requestId: result.requestId, + messageId: result.messageId, + idempotencyKey: result.idempotencyKey, + status: "accepted", + deliveryId: result.deliveryId + }, result]; +} +function parseHash(value, field) { + if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid Boss ledger ${field}`); + return value; +} +function parseEntry(value, version) { + assertBossCanonicalData(value, "$.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss ledger entry"); + const entry = value; + const legacyExpiry = version === EXPIRING_BOSS_CONTROL_LEDGER_VERSION; + if (legacyExpiry && (typeof entry.expiresAt !== "number" || !Number.isSafeInteger(entry.expiresAt))) { + throw new Error("Invalid Boss ledger expiry"); + } + const base = { + scope: parseHash(entry.scope, "scope"), + fingerprint: parseHash(entry.fingerprint, "fingerprint") + }; + const baseKeys = legacyExpiry ? ["scope", "fingerprint", "expiresAt", "state"] : ["scope", "fingerprint", "state"]; + if (entry.state === "accepted" && exactStringKeys(entry, [...baseKeys, "deliveryId"]) && typeof entry.deliveryId === "string" && entry.deliveryId.length > 0) return { ...base, state: "accepted", deliveryId: entry.deliveryId }; + if (entry.state === "terminal" && exactStringKeys(entry, [...baseKeys, "result"])) { + return { ...base, state: "terminal", result: parseBossControlResult(entry.result) }; + } + throw new Error("Invalid Boss ledger state discriminant"); +} +var BossControlResultLedger = class { + constructor(path, now = Date.now) { + this.path = path; + this.now = now; + const loaded = this.load(); + this.state = loaded.state; + if (loaded.migrated) this.persist(); + } + path; + now; + state; + lookup(scope, fingerprint) { + const entry = this.state.entries.find((candidate) => candidate.scope === scope); + if (!entry) return { status: "miss" }; + if (entry.fingerprint !== fingerprint) return { status: "conflict" }; + return entry.state === "accepted" ? { status: "accepted", deliveryId: entry.deliveryId } : { status: "replay", result: structuredClone(entry.result) }; + } + recordAccepted(scope, fingerprint, deliveryId) { + if (!/^[a-f0-9]{64}$/.test(scope) || !/^[a-f0-9]{64}$/.test(fingerprint) || !deliveryId) { + throw new Error("Invalid Boss accepted-state binding"); + } + const existing = this.state.entries.find((entry) => entry.scope === scope); + if (existing) { + if (existing.fingerprint !== fingerprint || existing.state !== "accepted" || existing.deliveryId !== deliveryId) { + throw new Error("Boss idempotency scope is already bound to a different canonical state"); + } + return; + } + this.reserveCapacity(); + this.state.entries.push({ scope, fingerprint, state: "accepted", deliveryId }); + this.persist(); + } + recordTerminal(scope, fingerprint, resultValue) { + const result = parseBossControlResult(resultValue); + canonicalJson2(result); + const existing = this.state.entries.find((entry) => entry.scope === scope); + if (existing?.fingerprint !== void 0 && existing.fingerprint !== fingerprint) { + throw new Error("Boss idempotency scope is already bound to a different canonical request"); + } + if (existing?.state === "terminal") { + const existingStable = { ...existing.result, requestId: "", messageId: "" }; + const resultStable = { ...result, requestId: "", messageId: "" }; + if (canonicalJson2(existingStable) !== canonicalJson2(resultStable)) { + throw new Error("Boss idempotency scope is already bound to a different canonical result"); + } + return; + } + if (result.status === "delivered" || result.deliveryId !== void 0) { + if (existing?.state !== "accepted" || existing.deliveryId !== result.deliveryId) { + throw new Error("Boss terminal delivery requires the matching durable accepted state"); + } + } else if (existing?.state === "accepted") { + throw new Error("A terminal result after acceptance must carry the accepted deliveryId"); + } + const terminal = { + scope, + fingerprint, + state: "terminal", + result + }; + if (existing) this.state.entries[this.state.entries.indexOf(existing)] = terminal; + else { + this.reserveCapacity(); + this.state.entries.push(terminal); + } + this.persist(); + } + reserveCapacity() { + if (this.state.entries.length >= MAX_BOSS_CONTROL_RESULTS) { + throw new Error("Durable Boss control ledger is full"); + } + } + load() { + if (!existsSync2(this.path)) { + return { state: { version: BOSS_CONTROL_LEDGER_VERSION, entries: [] }, migrated: false }; + } + try { + const parsed = JSON.parse(readFileSync4(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlLedger"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed; + if (!exactStringKeys(state, ["version", "entries"]) || state.version !== BOSS_CONTROL_LEDGER_VERSION && state.version !== EXPIRING_BOSS_CONTROL_LEDGER_VERSION || !Array.isArray(state.entries)) throw new Error("invalid ledger state"); + return { + state: { + version: BOSS_CONTROL_LEDGER_VERSION, + entries: state.entries.map((entry) => parseEntry(entry, state.version)) + }, + migrated: state.version === EXPIRING_BOSS_CONTROL_LEDGER_VERSION + }; + } catch (error) { + const corruptPath = `${this.path}.corrupt-${this.now()}`; + renameSync2(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control ledger was corrupt and quarantined at ${corruptPath}`, { cause: error }); + } + } + persist() { + writeDurableJson(this.path, this.state); + } +}; + // broker/broker.ts var INTERCOM_DIR = getIntercomDirPath(); var LISTEN_TARGET = getBrokerListenTarget(); @@ -890,6 +1211,7 @@ var ASK_STATE_PATH = getBrokerAskStateFilePath(INTERCOM_DIR); var ACCESS_STATE_PATH = getBrokerAccessStateFilePath(INTERCOM_DIR); var ADMIN_CREDENTIAL_PATH = getBrokerAdminCredentialFilePath(INTERCOM_DIR); var AUDIT_PATH = getBrokerAuditFilePath(INTERCOM_DIR); +var BOSS_CONTROL_LEDGER_PATH = join2(INTERCOM_DIR, "boss-control-results.json"); var BROKER_STATE_ID = randomUUID3(); var MAX_SESSIONS = 128; var MAX_UNREGISTERED_CONNECTIONS = 32; @@ -963,6 +1285,13 @@ function isSessionRegistration(value) { if (typeof session.cwd !== "string" || session.cwd.length === 0 || session.cwd.length > MAX_SESSION_CWD_LENGTH || typeof session.model !== "string" || session.model.length === 0 || session.model.length > MAX_SESSION_MODEL_LENGTH || typeof session.pid !== "number" || !Number.isFinite(session.pid) || typeof session.startedAt !== "number" || !Number.isFinite(session.startedAt) || typeof session.lastActivity !== "number" || !Number.isFinite(session.lastActivity)) { return false; } + if (session.boss !== void 0) { + try { + parseBossParticipantRegistrationMetadata(session.boss); + } catch { + return false; + } + } if (session.name !== void 0 && (typeof session.name !== "string" || session.name.length > MAX_SESSION_NAME_LENGTH)) { return false; } @@ -983,6 +1312,8 @@ var IntercomBroker = class { pendingDeliveries = /* @__PURE__ */ new Map(); pendingDeliveryKeys = /* @__PURE__ */ new Map(); recentDeliveries = /* @__PURE__ */ new Map(); + pendingBossControls = /* @__PURE__ */ new Map(); + pendingBossControlKeys = /* @__PURE__ */ new Map(); connections = /* @__PURE__ */ new Set(); unregisteredConnections = /* @__PURE__ */ new Set(); server; @@ -992,11 +1323,13 @@ var IntercomBroker = class { askTimeoutMs = getAskTimeoutMs(); accessRegistry; audit; + bossControlLedger; constructor() { ensureIntercomRuntimeDir(INTERCOM_DIR); acquireBrokerOwnership(OWNER_PATH); this.accessRegistry = new RemoteAccessRegistry(ACCESS_STATE_PATH); this.audit = new BrokerAuditLog(AUDIT_PATH); + this.bossControlLedger = new BossControlResultLedger(BOSS_CONTROL_LEDGER_PATH); this.accessRegistry.ensureAdminCredential(ADMIN_CREDENTIAL_PATH); this.loadAskEdges(); if (typeof LISTEN_TARGET === "string" && process.platform !== "win32") { @@ -1183,6 +1516,27 @@ var IntercomBroker = class { sendDeliveryFailure(socket, messageId, accepted, code, reason) { writeMessage(socket, { type: "delivery_failed", messageId, accepted, code, reason }); } + sendBossControlFailure(socket, requestId, messageId, idempotencyKey, code, reason, deliveryId, ledgerBinding, acknowledgeAcceptedRecovery = false) { + const result = { + type: "boss_control_result", + requestId, + messageId, + idempotencyKey, + status: "rejected", + delivered: false, + code, + reason, + ...deliveryId === void 0 ? {} : { deliveryId } + }; + if (ledgerBinding) { + this.bossControlLedger.recordTerminal(ledgerBinding.scope, ledgerBinding.fingerprint, result); + } + if (acknowledgeAcceptedRecovery) { + for (const frame of bossControlAcceptedRecoveryFrames(result)) writeMessage(socket, frame); + } else { + writeMessage(socket, result); + } + } scheduleShutdownCheck() { if (this.shutdownTimer) return; this.shutdownTimer = setTimeout(() => { @@ -1194,9 +1548,11 @@ var IntercomBroker = class { }, 5e3); } handleMessage(socket, origin, msg, currentId, setId) { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes2.isProxy(msg)) { throw new Error("Invalid client message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if (typeDescriptor === void 0 || !typeDescriptor.enumerable || !Object.hasOwn(typeDescriptor, "value") || typeof typeDescriptor.value !== "string") throw new Error("Invalid client message"); const clientMessage = msg; const requiresEndpointAuth = typeof LISTEN_TARGET !== "string"; const hasEndpointAuth = clientMessage.stateId === BROKER_STATE_ID; @@ -1213,7 +1569,8 @@ var IntercomBroker = class { protocol: INTERCOM_PROTOCOL_NAME, version: INTERCOM_PROTOCOL_VERSION, endpoint: origin, - remoteAccess: this.remoteAccessContract() + remoteAccess: this.remoteAccessContract(), + ...bossCapabilityAdvertisement() === void 0 ? {} : { capabilities: bossCapabilityAdvertisement() } }); return; } @@ -1240,8 +1597,17 @@ var IntercomBroker = class { } switch (clientMessage.type) { case "register": { + try { + parseExactRegistrationFrame(clientMessage); + } catch (error) { + this.sendError(socket, "BOSS_CONTRACT_MISMATCH", error instanceof Error ? error.message : "Registration contract is invalid"); + socket.end(); + break; + } if (!isSessionRegistration(clientMessage.session)) { - throw new Error("Invalid register message"); + this.sendError(socket, "BOSS_CONTRACT_MISMATCH", "Registration session contract is invalid"); + socket.end(); + break; } if (clientMessage.protocol !== INTERCOM_PROTOCOL_NAME || clientMessage.version !== INTERCOM_PROTOCOL_VERSION) { this.sendError( @@ -1255,6 +1621,17 @@ var IntercomBroker = class { if (currentId) { throw new Error("Received duplicate register message"); } + if (clientMessage.session.boss !== void 0) { + if (origin !== "local") { + this.sendError(socket, "ACCESS_DENIED", "Boss participants require the protected local broker endpoint"); + } else if (bossCapabilityAdvertisement() === void 0) { + this.sendError(socket, "BOSS_FEATURE_UNAVAILABLE", "boss-run-v1 is not advertised by this broker"); + } else { + this.sendError(socket, "BOSS_FEATURE_UNAVAILABLE", "Boss participant credential binding is not installed"); + } + socket.end(); + break; + } let id; let remotePrincipal; let issuedSessionCredential; @@ -1622,6 +1999,139 @@ var IntercomBroker = class { this.sendDeliveryFailure(socket, message.id, false, "SESSION_NOT_FOUND", "Session not found"); break; } + case "boss_control": { + if (!currentId) throw new Error("Received boss_control before register"); + const requestId = clientMessage.requestId; + const requestedTarget = clientMessage.to; + if (typeof requestId !== "string" || requestId.length === 0 || requestId.length > MAX_MESSAGE_ID_LENGTH || typeof requestedTarget !== "string" || requestedTarget.length === 0 || requestedTarget.length > MAX_TARGET_LENGTH) throw new Error("Invalid boss_control routing metadata"); + const sender = this.sessions.get(currentId); + let envelope; + let controlKind; + try { + if (!sender || sender.socket !== socket) throw new Error("Boss sender session not found"); + envelope = assertBossControlSender(sender.info, clientMessage.envelope); + controlKind = bossControlKind(envelope).controlKind; + if (requestId !== envelope.messageId) { + throw new Error("requestId must equal the canonical Boss envelope messageId"); + } + } catch (error) { + this.sendBossControlFailure( + socket, + requestId, + requestId, + requestId, + "INVALID_CONTROL", + error instanceof Error ? error.message : "Invalid Boss control envelope" + ); + break; + } + const key = this.bossControlKey(currentId, envelope); + const fingerprint = this.bossControlFingerprint(requestedTarget, envelope); + const prior = this.bossControlLedger.lookup(key, fingerprint); + if (prior.status === "replay") { + for (const frame of bossControlReplayFrames(prior.result, envelope.messageId)) writeMessage(socket, frame); + break; + } + if (prior.status === "conflict") { + this.sendBossControlFailure( + socket, + requestId, + envelope.messageId, + envelope.idempotencyKey, + "IDEMPOTENCY_CONFLICT", + "Boss idempotency key is durably bound to a different canonical request" + ); + break; + } + const exactTarget = exactBossSessionTarget(this.sessions, requestedTarget); + const correlated = hasAuthoritativeBossControlCorrelation(); + const target = exactTarget && authorizeSessionAction( + Array.from(this.sessions.values(), (session) => session.info), + currentId, + "control", + exactTarget.info.id, + { controlKind, correlated } + ).allowed ? exactTarget : void 0; + if (!target) { + const acceptedDeliveryId = prior.status === "accepted" ? prior.deliveryId : void 0; + this.sendBossControlFailure( + socket, + requestId, + envelope.messageId, + envelope.idempotencyKey, + exactTarget ? "POLICY_DENIED" : "SESSION_NOT_FOUND", + exactTarget ? "Boss control routing requires authoritative correlation evidence" : "Boss control target session ID was not found", + acceptedDeliveryId, + { scope: key, fingerprint }, + acceptedDeliveryId !== void 0 + ); + break; + } + const existingDeliveryId = this.pendingBossControlKeys.get(key); + if (existingDeliveryId) { + const existing = this.pendingBossControls.get(existingDeliveryId); + if (!existing || existing.fingerprint !== fingerprint) { + this.sendBossControlFailure(socket, requestId, envelope.messageId, envelope.idempotencyKey, "IDEMPOTENCY_CONFLICT", "Boss idempotency key is already bound to a different canonical request"); + break; + } + if (existing.messageId !== envelope.messageId) { + existing.requestId = requestId; + existing.messageId = envelope.messageId; + existing.envelope = envelope; + existing.senderSocket = socket; + writeMessage(existing.recipientSocket, { type: "boss_control", deliveryId: existing.deliveryId, from: sender.info, envelope }); + } + writeMessage(socket, { + type: "boss_control_ack", + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "accepted", + deliveryId: existing.deliveryId + }); + break; + } + const deliveryId = prior.status === "accepted" ? prior.deliveryId : randomUUID3(); + if (prior.status === "miss") { + this.bossControlLedger.recordAccepted(key, fingerprint, deliveryId); + } + const timeout = setTimeout(() => { + this.failPendingBossControl(deliveryId, "DELIVERY_TIMEOUT", "Recipient did not acknowledge the Boss control envelope in time"); + }, DELIVERY_ACK_TIMEOUT_MS); + timeout.unref?.(); + this.pendingBossControls.set(deliveryId, { + deliveryId, + key, + fingerprint, + requestId, + messageId: envelope.messageId, + envelope, + from: currentId, + to: target.info.id, + senderSocket: socket, + recipientSocket: target.socket, + timeout + }); + this.pendingBossControlKeys.set(key, deliveryId); + writeMessage(socket, { + type: "boss_control_ack", + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "accepted", + deliveryId + }); + writeMessage(target.socket, { type: "boss_control", deliveryId, from: sender.info, envelope }); + break; + } + case "boss_control_received": { + if (!currentId) throw new Error("Received boss_control_received before register"); + if (typeof clientMessage.deliveryId !== "string" || typeof clientMessage.messageId !== "string" || typeof clientMessage.idempotencyKey !== "string") { + throw new Error("Invalid boss_control_received message"); + } + this.acknowledgePendingBossControl(clientMessage.deliveryId, clientMessage.messageId, clientMessage.idempotencyKey, currentId, socket); + break; + } case "message_received": { if (!currentId) { throw new Error("Received message_received before register"); @@ -2035,13 +2545,14 @@ var IntercomBroker = class { return false; } } - isAuthorized(actorId, action, targetId) { + isAuthorized(actorId, action, targetId, bossContext) { if (!this.isCurrentPrincipal(actorId) || !this.isCurrentPrincipal(targetId)) return false; return authorizeSessionAction( Array.from(this.sessions.values(), (session) => session.info), actorId, action, - targetId + targetId, + bossContext ).allowed; } broadcastVisible(message, subject, exclude) { @@ -2154,11 +2665,11 @@ var IntercomBroker = class { return timeout; } loadAskEdges() { - if (!existsSync2(ASK_STATE_PATH)) { + if (!existsSync3(ASK_STATE_PATH)) { return; } try { - const parsed = JSON.parse(readFileSync4(ASK_STATE_PATH, "utf-8")); + const parsed = JSON.parse(readFileSync5(ASK_STATE_PATH, "utf-8")); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error("expected an object"); } @@ -2199,7 +2710,7 @@ var IntercomBroker = class { this.askEdges.clear(); try { const corruptPath = `${ASK_STATE_PATH}.corrupt-${Date.now()}`; - renameSync2(ASK_STATE_PATH, corruptPath); + renameSync3(ASK_STATE_PATH, corruptPath); restrictIntercomRuntimeFile(corruptPath); } catch { } @@ -2234,6 +2745,84 @@ var IntercomBroker = class { } return count; } + bossControlKey(fromSessionId, envelope) { + return canonicalHash("agent-intercom-codex/boss-control/idempotency-scope/v1", { + fromSessionId, + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey + }); + } + bossControlFingerprint(toSessionId, envelope) { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/request/v1", { toSessionId, envelope: stableEnvelope }); + } + acknowledgePendingBossControl(deliveryId, messageId, idempotencyKey, sessionId, socket) { + const pending = this.pendingBossControls.get(deliveryId); + if (!pending || pending.to !== sessionId || pending.recipientSocket !== socket || pending.messageId !== messageId || pending.envelope.idempotencyKey !== idempotencyKey) return; + const sender = this.sessions.get(pending.from); + const recipient = this.sessions.get(pending.to); + const { controlKind } = bossControlKind(pending.envelope); + if (!sender || !recipient || !authorizeSessionAction( + Array.from(this.sessions.values(), (session) => session.info), + pending.from, + "control", + pending.to, + { controlKind, correlated: hasAuthoritativeBossControlCorrelation() } + ).allowed) { + this.failPendingBossControl(deliveryId, "POLICY_DENIED", "Boss control authorization changed before acknowledgement"); + return; + } + if (sender.socket === pending.senderSocket) { + const result = { + type: "boss_control_result", + requestId: pending.requestId, + messageId: pending.messageId, + idempotencyKey: pending.envelope.idempotencyKey, + status: "delivered", + deliveryId, + delivered: true + }; + this.bossControlLedger.recordTerminal(pending.key, pending.fingerprint, result); + clearTimeout(pending.timeout); + this.pendingBossControls.delete(deliveryId); + this.pendingBossControlKeys.delete(pending.key); + writeMessage(sender.socket, result); + } + } + failPendingBossControl(deliveryId, code, reason) { + const pending = this.pendingBossControls.get(deliveryId); + if (!pending) return; + const sender = this.sessions.get(pending.from); + const result = { + type: "boss_control_result", + requestId: pending.requestId, + messageId: pending.messageId, + idempotencyKey: pending.envelope.idempotencyKey, + status: "rejected", + delivered: false, + code, + reason, + deliveryId + }; + this.bossControlLedger.recordTerminal(pending.key, pending.fingerprint, result); + clearTimeout(pending.timeout); + this.pendingBossControls.delete(deliveryId); + this.pendingBossControlKeys.delete(pending.key); + if (sender?.socket === pending.senderSocket) writeMessage(sender.socket, result); + } + clearPendingBossControlsForSession(sessionId, socket) { + for (const pending of Array.from(this.pendingBossControls.values())) { + if (pending.to === sessionId && pending.recipientSocket === socket) { + this.failPendingBossControl(pending.deliveryId, "RECIPIENT_DISCONNECTED", "Boss control recipient disconnected before acknowledgement"); + } else if (pending.from === sessionId && pending.senderSocket === socket) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(pending.deliveryId); + this.pendingBossControlKeys.delete(pending.key); + } + } + } acknowledgePendingDelivery(deliveryId, sessionId, socket) { const pending = this.pendingDeliveries.get(deliveryId); if (!pending || pending.to !== sessionId || pending.recipientSocket !== socket) { @@ -2302,6 +2891,7 @@ var IntercomBroker = class { } } clearPendingDeliveriesForSession(sessionId, socket) { + this.clearPendingBossControlsForSession(sessionId, socket); for (const delivery of Array.from(this.pendingDeliveries.values())) { if (delivery.to === sessionId && delivery.recipientSocket === socket) { this.failPendingDelivery(delivery.id, "RECIPIENT_DISCONNECTED", "Recipient disconnected before acknowledging the message"); @@ -2344,6 +2934,11 @@ var IntercomBroker = class { } this.pendingDeliveries.clear(); this.pendingDeliveryKeys.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + } + this.pendingBossControls.clear(); + this.pendingBossControlKeys.clear(); for (const edge of this.askEdges.values()) { clearTimeout(edge.timeout); } diff --git a/dist/build-info.json b/dist/build-info.json index 2eea83c..b78151c 100644 --- a/dist/build-info.json +++ b/dist/build-info.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "package": "@dataforxyz/agent-intercom-codex", "version": "0.10.0", - "sourceSha256": "28cbe04c291ec9ca89e519b437e41d7a7c359f85cf99d2fcf3e6cb0f74dcee2c", + "sourceSha256": "e3924d8a81ca3579d920e6938f77fe75f36c8eeb02d45b49b2d940b7a67b6410", "targets": [ "codex-server", "broker", diff --git a/dist/codex-server.mjs b/dist/codex-server.mjs index b50aa8d..adae97f 100755 --- a/dist/codex-server.mjs +++ b/dist/codex-server.mjs @@ -1,12 +1,12 @@ #!/usr/bin/env node -process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=codex-server sourceSha256=28cbe04c291ec9ca89e519b437e41d7a7c359f85cf99d2fcf3e6cb0f74dcee2c\n"); +process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=codex-server sourceSha256=e3924d8a81ca3579d920e6938f77fe75f36c8eeb02d45b49b2d940b7a67b6410\n"); // codex/server.ts import readline from "node:readline"; import { stdin, stdout } from "node:process"; // codex/runtime.ts -import { randomUUID as randomUUID4, createHash as createHash2 } from "crypto"; +import { randomUUID as randomUUID4, createHash as createHash3 } from "crypto"; import { spawnSync } from "child_process"; import { basename } from "path"; import { cwd as processCwd } from "process"; @@ -15,166 +15,11 @@ import { cwd as processCwd } from "process"; import { EventEmitter } from "events"; import net from "net"; import { randomUUID as randomUUID2 } from "crypto"; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy.js -var POLICY_SEMANTICS_VERSION = 2; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy-vectors.js -var localRoot = { - id: "local-root", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-root" -}; -var localPeer = { - id: "local-peer", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-peer" -}; -var remoteManager = { - id: "remote-manager", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "local-root", - rootSessionId: "local-root" -}; -var remoteChild = { - id: "remote-child", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var remoteSibling = { - id: "remote-sibling", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var POLICY_VECTORS = [ - { - name: "local sessions remain public", - principals: [localRoot, localPeer], - actorId: "local-root", - action: "send", - targetId: "local-peer", - expectedAllowed: true, - expectedReasonOrCode: "local-public" - }, - { - name: "remote manager can reach direct local parent", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "local parent can reach direct remote child", - principals: [localRoot, remoteManager], - actorId: "local-root", - action: "ask", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "remote child can reach its local root through the ancestor chain", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-chain" - }, - { - name: "remote siblings cannot communicate in phase one", - principals: [localRoot, remoteManager, remoteChild, remoteSibling], - actorId: "remote-child", - action: "discover", - targetId: "remote-sibling", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "unrelated local session cannot discover remote principal", - principals: [localRoot, localPeer, remoteManager], - actorId: "local-peer", - action: "discover", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal cannot reach unrelated local session", - principals: [localRoot, localPeer, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-peer", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote manager may inspect its descendant subtree", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-manager", - action: "inspect_tree", - targetId: "remote-child", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-control" - }, - { - name: "remote child cannot revoke its ancestor", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "revoke", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal may request attenuated delegation under itself", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "delegate_child", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "self" - }, - { - name: "revoked principal cannot communicate", - principals: [localRoot, { ...remoteManager, state: "revoked" }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: false, - expectedReasonOrCode: "REVOKED_PRINCIPAL" - }, - { - name: "stale actor generation cannot send", - principals: [localRoot, { ...remoteManager, generation: 2 }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - context: { actorGeneration: 1 }, - expectedAllowed: false, - expectedReasonOrCode: "STALE_GENERATION" - } -]; -var POLICY_SEMANTICS_HASH = "f3b00e503631bc91123aedfbcf1df72cc9913e1893c09728b2c598f3dcdfdfe0"; +import { POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION } from "@dataforxyz/agent-intercom-core"; +import { + BOSS_RUN_FEATURE as BOSS_RUN_FEATURE2, + parseBrokerCapabilityAdvertisement as parseBrokerCapabilityAdvertisement2 +} from "@dataforxyz/agent-intercom-core/boss"; // broker/framing.ts var MAX_FRAME_BYTES = 1024 * 1024; @@ -425,8 +270,464 @@ var PersistentOutboundOutbox = class { } }; +// boss-control-outbox.ts +import { createHash as createHash2 } from "node:crypto"; +import { chmodSync as chmodSync3, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3 } from "node:fs"; +import { join as join3 } from "node:path"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; +import { parseBossControlEnvelope as parseBossControlEnvelope2 } from "@dataforxyz/agent-intercom-core/boss"; + +// broker/boss-adapter.ts +import { + BOSS_CAPABILITY_FEATURE_DIGEST, + BOSS_CONTROL_ENVELOPE_VERSION, + BOSS_POLICY_PRINCIPAL_VERSION, + BOSS_POLICY_SEMANTICS_HASH, + BOSS_RUN_FEATURE, + BOSS_RUN_FEATURE_CONTRACT, + BOSS_RUN_FEATURE_SEMANTICS_HASH, + BOSS_RUN_FEATURE_VERSION, + BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + BROKER_FEATURE_ATTESTATION_VERSION, + INTERCOM_BASE_PROTOCOL_VERSION, + authorizeFeatureAware, + brokerFeatureSetHash, + parseBossControlEnvelope, + parseBossParticipantBinding, + parseBossParticipantCredentialEnvelope, + parseBossRunFeatureContract, + parseBrokerCapabilityAdvertisement, + parseParticipantState, + parseWorkerIdentityV2 +} from "@dataforxyz/agent-intercom-core/boss"; +import { + ContractValidationError, + assertExactKeys, + assertRecord, + canonicalJson +} from "@dataforxyz/agent-intercom-core/canonical"; +import { types as nodeUtilTypes } from "node:util"; +var BOSS_ADVERTISEMENT_PREDICATES = [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth" +]; +var DORMANT_BOSS_ADVERTISEMENT_READINESS = Object.freeze({ + protectedProvider: false, + brokerIdentity: false, + credentialRegistry: false, + authorityTransitions: false, + participantHealth: false +}); +var ORDINARY_SESSION_REGISTRATION_KEYS = [ + "cwd", + "model", + "pid", + "startedAt", + "lastActivity" +]; +var OPTIONAL_SESSION_REGISTRATION_KEYS = ["name", "status", "runtimeInstanceId"]; +function assertBossCanonicalData(value, path = "$", seen = /* @__PURE__ */ new WeakSet()) { + if (typeof value !== "object" || value === null) return; + if (nodeUtilTypes.isProxy(value)) { + throw new ContractValidationError(path, "proxies are not supported"); + } + if (seen.has(value)) throw new ContractValidationError(path, "cyclic values are not supported"); + seen.add(value); + if (Array.isArray(value)) { + if (Object.getPrototypeOf(value) !== Array.prototype) { + throw new ContractValidationError(path, "must use the exact Array prototype"); + } + const ownKeys = Reflect.ownKeys(value); + const expectedKeys = /* @__PURE__ */ new Set(["length"]); + for (let index = 0; index < value.length; index += 1) expectedKeys.add(String(index)); + if (ownKeys.length !== expectedKeys.size || ownKeys.some((key) => !expectedKeys.has(key))) { + throw new ContractValidationError(path, "must be a dense array without symbols or extra properties"); + } + for (let index = 0; index < value.length; index += 1) { + if (!Object.hasOwn(value, index)) { + throw new ContractValidationError(`${path}[${index}]`, "sparse array holes are not supported"); + } + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}[${index}]`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}[${index}]`, seen); + } + return; + } + if (Object.getPrototypeOf(value) !== Object.prototype) { + throw new ContractValidationError(path, "must use the exact Object prototype"); + } + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") throw new ContractValidationError(path, "symbol properties are not supported"); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}.${key}`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}.${key}`, seen); + } +} +function parseBossAdvertisementReadiness(value) { + assertBossCanonicalData(value, "$.readiness"); + assertRecord(value); + assertExactKeys(value, BOSS_ADVERTISEMENT_PREDICATES); + const parsed = {}; + for (const predicate of BOSS_ADVERTISEMENT_PREDICATES) { + const enabled = ownDataValue(value, predicate); + if (typeof enabled !== "boolean") { + throw new ContractValidationError(`$.readiness.${predicate}`, "must be a boolean"); + } + parsed[predicate] = enabled; + } + return parsed; +} +function missingBossAdvertisementPredicates(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + const parsed = parseBossAdvertisementReadiness(readiness); + return BOSS_ADVERTISEMENT_PREDICATES.filter((predicate) => parsed[predicate] !== true); +} +function bossCapabilityAdvertisement(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + if (missingBossAdvertisementPredicates(readiness).length > 0) return void 0; + const features = [{ + version: BROKER_FEATURE_ATTESTATION_VERSION, + feature: BOSS_RUN_FEATURE, + featureVersion: BOSS_RUN_FEATURE_VERSION, + semanticsHash: BOSS_RUN_FEATURE_SEMANTICS_HASH, + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }]; + return parseBrokerCapabilityAdvertisement({ + baseProtocolVersion: INTERCOM_BASE_PROTOCOL_VERSION, + features, + protocolFeatureContractHash: BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + featureSetHash: brokerFeatureSetHash(features), + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }); +} +function optionalOwnDataValue(value, key) { + assertRecord(value); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0) return void 0; + if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`$.${key}`, "must be an own enumerable data property"); + } + return descriptor.value; +} +function ownDataValue(value, key) { + const result = optionalOwnDataValue(value, key); + if (result === void 0) throw new ContractValidationError(`$.${key}`, "is required"); + return result; +} +function parseBossParticipantRegistrationMetadata(value) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys(value, ["featureContract", "credential"]); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly negotiate boss-run-v1 over base protocol v3"); + } + const credential = parseBossParticipantCredentialEnvelope(ownDataValue(value, "credential")); + if (credential.namespace !== featureContract.feature) { + throw new ContractValidationError("$.credential.namespace", "must match the negotiated feature namespace"); + } + return { featureContract, credential }; +} +function exactRegistrationKind(session, value) { + assertBossCanonicalData(session, "$.session"); + assertRecord(session); + const boss = optionalOwnDataValue(session, "boss"); + if (boss === void 0) { + if (value === void 0) return "ordinary"; + throw new ContractValidationError("$.registrationKind", "must be absent when Boss metadata is absent"); + } + if (value !== "boss") { + throw new ContractValidationError("$.registrationKind", "must be boss when Boss metadata is present"); + } + return "boss"; +} +function parseExactRegistrationFrame(value) { + assertBossCanonicalData(value, "$.register"); + assertRecord(value); + const session = ownDataValue(value, "session"); + assertRecord(session); + const registrationKind = optionalOwnDataValue(value, "registrationKind"); + const kind = exactRegistrationKind(session, registrationKind); + if (kind === "ordinary") { + assertExactKeys(value, ["type", "protocol", "version", "session"], ["sessionId", "stateId", "access"]); + assertExactKeys(session, ORDINARY_SESSION_REGISTRATION_KEYS, OPTIONAL_SESSION_REGISTRATION_KEYS); + } else { + assertExactKeys(value, ["type", "registrationKind", "protocol", "version", "session"], ["sessionId", "stateId"]); + assertExactKeys(session, [...ORDINARY_SESSION_REGISTRATION_KEYS, "boss"], OPTIONAL_SESSION_REGISTRATION_KEYS); + parseBossParticipantRegistrationMetadata(ownDataValue(session, "boss")); + } + if (ownDataValue(value, "type") !== "register") { + throw new ContractValidationError("$.register.type", "must be register"); + } + return value; +} +function parseExactRegisteredFrame(value, expected) { + assertBossCanonicalData(value, "$.registered"); + assertRecord(value); + if (expected === "boss") { + assertExactKeys(value, ["type", "registrationKind", "sessionId", "protocol", "version", "capabilities", "boss"]); + if (ownDataValue(value, "registrationKind") !== "boss") { + throw new ContractValidationError("$.registered.registrationKind", "must be boss"); + } + const sessionId = ownDataValue(value, "sessionId"); + if (typeof sessionId !== "string" || sessionId.length === 0) { + throw new ContractValidationError("$.registered.sessionId", "must be a non-empty string"); + } + const advertisement = parseBrokerCapabilityAdvertisement(ownDataValue(value, "capabilities")); + const expectedAdvertisement = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true + }); + const bossFeature = advertisement.features.find((feature) => feature.feature === BOSS_RUN_FEATURE); + if (bossFeature === void 0 || canonicalJson(bossFeature) !== canonicalJson(expectedAdvertisement.features[0]) || advertisement.baseProtocolVersion !== expectedAdvertisement.baseProtocolVersion || advertisement.protocolFeatureContractHash !== expectedAdvertisement.protocolFeatureContractHash || advertisement.controlEnvelopeVersion !== expectedAdvertisement.controlEnvelopeVersion || advertisement.capabilityDigest !== expectedAdvertisement.capabilityDigest) throw new ContractValidationError("$.registered.capabilities", "must exactly echo the requested boss-run-v1 contract"); + parseBossParticipantBindingMetadata(ownDataValue(value, "boss"), sessionId); + } else if (expected === "ordinary-remote") { + assertExactKeys(value, ["type", "sessionId", "protocol", "version", "remoteAccess", "access"]); + } else { + assertExactKeys(value, ["type", "sessionId", "protocol", "version"]); + } + if (ownDataValue(value, "type") !== "registered") { + throw new ContractValidationError("$.registered.type", "must be registered"); + } + return value; +} +function parseBossParticipantBindingMetadata(value, expectedSessionId) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys( + value, + ["featureContract", "binding", "brokerIdentityVerified"], + ["assignedParticipantIds", "requestingPrincipalId", "workerIdentity", "participantState"] + ); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly bind boss-run-v1 over base protocol v3"); + } + const binding = parseBossParticipantBinding(ownDataValue(value, "binding")); + if (ownDataValue(value, "brokerIdentityVerified") !== true) { + throw new ContractValidationError("$.brokerIdentityVerified", "must be true for a broker-owned Boss binding"); + } + if (expectedSessionId !== void 0 && binding.sessionId !== expectedSessionId) { + throw new ContractValidationError("$.binding.sessionId", "must match the registered intercom session"); + } + const rawAssignedParticipantIds = optionalOwnDataValue(value, "assignedParticipantIds"); + let assignedParticipantIds; + if (rawAssignedParticipantIds !== void 0) { + if (binding.role !== "manager" || !Array.isArray(rawAssignedParticipantIds) || rawAssignedParticipantIds.some((entry) => typeof entry !== "string" || entry.length === 0) || new Set(rawAssignedParticipantIds).size !== rawAssignedParticipantIds.length) { + throw new ContractValidationError("$.assignedParticipantIds", "must be a unique participant list present only for a Manager"); + } + assignedParticipantIds = rawAssignedParticipantIds; + } + if (binding.role === "manager" && assignedParticipantIds === void 0) { + throw new ContractValidationError("$.assignedParticipantIds", "is required for a Manager policy binding"); + } + const rawRequestingPrincipalId = optionalOwnDataValue(value, "requestingPrincipalId"); + if (binding.role === "council" !== (typeof rawRequestingPrincipalId === "string" && rawRequestingPrincipalId.length > 0)) { + throw new ContractValidationError("$.requestingPrincipalId", "is required exactly for a Council policy binding"); + } + const requestingPrincipalId = typeof rawRequestingPrincipalId === "string" ? rawRequestingPrincipalId : void 0; + const rawWorkerIdentity = optionalOwnDataValue(value, "workerIdentity"); + const rawParticipantState = optionalOwnDataValue(value, "participantState"); + if (rawWorkerIdentity === void 0 !== (rawParticipantState === void 0)) { + throw new ContractValidationError("$.workerIdentity", "workerIdentity and participantState must be supplied together"); + } + const workerIdentity = rawWorkerIdentity === void 0 ? void 0 : parseWorkerIdentityV2(rawWorkerIdentity); + const participantState = rawParticipantState === void 0 ? void 0 : parseParticipantState(rawParticipantState, "$.participantState"); + if (workerIdentity !== void 0 && (!("bossRunId" in workerIdentity) || workerIdentity.bossRunId !== binding.bossRunId || workerIdentity.participantId !== binding.participantId || workerIdentity.bindingEpoch !== binding.bindingEpoch)) throw new ContractValidationError("$.workerIdentity", "must match the broker-owned participant binding"); + return { + featureContract, + binding, + brokerIdentityVerified: true, + ...assignedParticipantIds === void 0 ? {} : { assignedParticipantIds: [...assignedParticipantIds] }, + ...requestingPrincipalId === void 0 ? {} : { requestingPrincipalId }, + ...workerIdentity === void 0 ? {} : { workerIdentity, participantState } + }; +} +var BOSS_CONTROL_KIND_BY_TYPE = { + "boss.assignment.created": "assignment_request", + "boss.assignment.accepted": "assignment_response", + "boss.assignment.checkpoint": "assignment_response", + "boss.assignment.submitted": "assignment_response", + "boss.assignment.rejected": "assignment_response", + "boss.assignment.cancelled": "lifecycle", + "boss.staffing.requested": "staffing", + "boss.staffing.resolved": "staffing", + "boss.review.requested": "review_request", + "boss.review.submitted": "review_result", + "boss.council.requested": "review_request", + "boss.council.submitted": "review_result", + "boss.proof.submitted": "proof", + "boss.worker.health": "health", + "boss.worker.blocked": "health", + "boss.worker.failed": "health", + "boss.worker.notice": "lifecycle", + "boss.worker.notice_delivery_failed": "lifecycle", + "boss.decision.required": "decision" +}; +function bossControlKind(envelopeValue) { + assertBossCanonicalData(envelopeValue); + const envelope = parseBossControlEnvelope(envelopeValue); + return { envelope, controlKind: BOSS_CONTROL_KIND_BY_TYPE[envelope.type] }; +} + +// boss-control-outbox.ts +var BOSS_CONTROL_OUTBOX_VERSION = 2; +var MAX_BOSS_CONTROL_OUTBOX_ENTRIES = 256; +function scope(envelope) { + return canonicalHash("agent-intercom-codex/boss-control/outbox-scope/v1", { + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey + }); +} +function fingerprint2(to, envelope) { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/outbox-request/v1", { to, envelope: stableEnvelope }); +} +function exactKeys(value, required, optional = []) { + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + const keys = Reflect.ownKeys(value); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseEntry(value) { + assertBossCanonicalData(value, "$.bossControlOutbox.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss outbox entry"); + const entry = value; + if (!exactKeys(entry, ["to", "envelope", "scope", "fingerprint", "queuedAt", "state"], ["deliveryId"])) { + throw new Error("Invalid Boss outbox entry fields"); + } + if (typeof entry.to !== "string" || entry.to.length === 0 || typeof entry.scope !== "string" || !/^[a-f0-9]{64}$/.test(entry.scope) || typeof entry.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(entry.fingerprint) || typeof entry.queuedAt !== "number" || !Number.isSafeInteger(entry.queuedAt) || entry.state !== "queued" && entry.state !== "accepted" || entry.state === "queued" && Object.hasOwn(entry, "deliveryId") || entry.state === "accepted" && (!Object.hasOwn(entry, "deliveryId") || typeof entry.deliveryId !== "string" || entry.deliveryId.length === 0)) throw new Error("Invalid Boss outbox entry binding"); + const envelope = parseBossControlEnvelope2(entry.envelope); + if (entry.scope !== scope(envelope) || entry.fingerprint !== fingerprint2(entry.to, envelope)) { + throw new Error("Boss outbox entry canonical binding mismatch"); + } + return { + to: entry.to, + envelope, + scope: entry.scope, + fingerprint: entry.fingerprint, + queuedAt: entry.queuedAt, + state: entry.state, + ...entry.deliveryId === void 0 ? {} : { deliveryId: entry.deliveryId } + }; +} +function fileName2(sessionId) { + return `${createHash2("sha256").update(sessionId).digest("hex")}.json`; +} +var PersistentBossControlOutbox = class { + path; + state; + constructor(sessionId, intercomDir = getIntercomDirPath()) { + ensureIntercomRuntimeDir(intercomDir); + const directory = join3(intercomDir, "boss-control-outbox"); + mkdirSync3(directory, { recursive: true, mode: INTERCOM_DIR_MODE }); + if (process.platform !== "win32") chmodSync3(directory, INTERCOM_DIR_MODE); + this.path = join3(directory, fileName2(sessionId)); + this.state = this.load(); + } + list() { + return structuredClone(this.state.entries); + } + find(idempotencyKey) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + return entry === void 0 ? void 0 : structuredClone(entry); + } + enqueue(to, envelopeValue) { + if (typeof to !== "string" || to.length === 0) throw new Error("Boss target session ID is required"); + assertBossCanonicalData(envelopeValue, "$.envelope"); + const envelope = parseBossControlEnvelope2(envelopeValue); + const candidateScope = scope(envelope); + const candidateFingerprint = fingerprint2(to, envelope); + const existing = this.state.entries.find((entry) => entry.scope === candidateScope); + if (existing) { + if (existing.fingerprint !== candidateFingerprint) { + throw new Error(`Boss idempotency key ${envelope.idempotencyKey} is queued with a different canonical request`); + } + if (existing.envelope.messageId !== envelope.messageId) { + existing.envelope = envelope; + existing.queuedAt = Date.now(); + this.persist(); + } + return "existing"; + } + if (this.state.entries.some((entry) => entry.envelope.messageId === envelope.messageId)) { + throw new Error(`Boss message ID ${envelope.messageId} is queued with a different idempotency scope`); + } + if (this.state.entries.length >= MAX_BOSS_CONTROL_OUTBOX_ENTRIES) throw new Error("Durable Boss control outbox is full"); + this.state.entries.push({ + to, + envelope, + scope: candidateScope, + fingerprint: candidateFingerprint, + queuedAt: Date.now(), + state: "queued" + }); + this.persist(); + return "added"; + } + markAccepted(idempotencyKey, messageId, deliveryId) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (!entry || entry.envelope.messageId !== messageId || !deliveryId) { + throw new Error("Boss acknowledgement does not match the durable outbox binding"); + } + if (entry.state === "accepted") { + if (entry.deliveryId !== deliveryId) throw new Error("Boss acknowledgement changed the durable deliveryId"); + return "already-accepted"; + } + entry.state = "accepted"; + entry.deliveryId = deliveryId; + this.persist(); + return "accepted"; + } + removeCorrelated(idempotencyKey, messageId, deliveryId) { + const index = this.state.entries.findIndex((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (index < 0) throw new Error("Boss terminal result has no durable outbox binding"); + const entry = this.state.entries[index]; + if (entry.envelope.messageId !== messageId) throw new Error("Boss terminal result messageId does not match the durable caller"); + if (deliveryId === void 0) { + if (entry.state !== "queued") throw new Error("Boss post-acceptance failure omitted the durable deliveryId"); + } else if (entry.state !== "accepted" || entry.deliveryId !== deliveryId) { + throw new Error("Boss terminal result arrived before the matching durable acknowledgement"); + } + this.state.entries.splice(index, 1); + this.persist(); + } + load() { + if (!existsSync2(this.path)) return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: [] }; + try { + const parsed = JSON.parse(readFileSync3(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlOutbox"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed; + if (!exactKeys(state, ["version", "entries"]) || state.version !== BOSS_CONTROL_OUTBOX_VERSION || !Array.isArray(state.entries)) { + throw new Error("invalid Boss outbox state"); + } + return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: state.entries.map(parseEntry) }; + } catch (error2) { + const corruptPath = `${this.path}.corrupt-${Date.now()}`; + renameSync3(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control outbox was corrupt and quarantined at ${corruptPath}`, { cause: error2 }); + } + } + persist() { + writeDurableJson(this.path, this.state); + } +}; + // broker/access-credential.ts -import { readFileSync as readFileSync3 } from "fs"; +import { readFileSync as readFileSync4 } from "fs"; var ACCESS_CREDENTIAL_ENV = "AGENT_INTERCOM_ACCESS_CREDENTIAL_PATH"; var ACCESS_CREDENTIAL_VERSION = 1; function nonEmptyString(value) { @@ -435,7 +736,7 @@ function nonEmptyString(value) { function loadRemoteAccessCredential(env = process.env) { const path = env[ACCESS_CREDENTIAL_ENV]?.trim(); if (!path) return void 0; - const parsed = JSON.parse(readFileSync3(path, "utf8")); + const parsed = JSON.parse(readFileSync4(path, "utf8")); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error(`Invalid Agent Intercom access credential at ${path}`); } @@ -468,7 +769,49 @@ function writeRemoteSessionCredential(path, sessionId, metadata) { }); } +// broker/boss-control-ledger.ts +import { canonicalJson as canonicalJson2 } from "@dataforxyz/agent-intercom-core/canonical"; +var BOSS_CONTROL_FAILURE_CODES = /* @__PURE__ */ new Set([ + "INVALID_CONTROL", + "IDEMPOTENCY_CONFLICT", + "SESSION_NOT_FOUND", + "POLICY_DENIED", + "RECIPIENT_DISCONNECTED", + "DELIVERY_TIMEOUT" +]); +function exactStringKeys(value, required, optional = []) { + const keys = Reflect.ownKeys(value); + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseBossControlResult(value) { + assertBossCanonicalData(value, "$.bossControlResult"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control result must be an exact plain object"); + } + const result = value; + const base = typeof result.requestId === "string" && result.requestId.length > 0 && result.messageId === result.requestId && typeof result.idempotencyKey === "string" && result.idempotencyKey.length > 0; + if (!base || result.type !== "boss_control_result") throw new Error("Invalid Boss control result binding"); + if (result.status === "delivered" && result.delivered === true && typeof result.deliveryId === "string" && result.deliveryId.length > 0 && exactStringKeys(result, ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "deliveryId"])) return result; + if (result.status === "rejected" && result.delivered === false && typeof result.code === "string" && BOSS_CONTROL_FAILURE_CODES.has(result.code) && typeof result.reason === "string" && result.reason.length > 0 && (!Object.hasOwn(result, "deliveryId") || typeof result.deliveryId === "string" && result.deliveryId.length > 0) && exactStringKeys( + result, + ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "code", "reason"], + ["deliveryId"] + )) return result; + throw new Error("Invalid Boss control result discriminant"); +} +function parseBossControlAck(value) { + assertBossCanonicalData(value, "$.bossControlAck"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control acknowledgement must be an exact plain object"); + } + const ack = value; + if (!exactStringKeys(ack, ["type", "requestId", "messageId", "idempotencyKey", "status", "deliveryId"]) || ack.type !== "boss_control_ack" || typeof ack.requestId !== "string" || ack.requestId.length === 0 || ack.messageId !== ack.requestId || typeof ack.idempotencyKey !== "string" || ack.idempotencyKey.length === 0 || ack.status !== "accepted" || typeof ack.deliveryId !== "string" || ack.deliveryId.length === 0) throw new Error("Invalid Boss control acknowledgement discriminant"); + return ack; +} + // broker/client.ts +import { types as nodeUtilTypes2 } from "node:util"; function toError(error2) { return error2 instanceof Error ? error2 : new Error(String(error2)); } @@ -538,6 +881,13 @@ function isSessionInfo(value) { for (const field of ["depth", "maxDepth", "maxChildren"]) { if (session[field] !== void 0 && (typeof session[field] !== "number" || !Number.isSafeInteger(session[field]))) return false; } + if (session.boss !== void 0) { + try { + parseBossParticipantBindingMetadata(session.boss, session.id); + } catch { + return false; + } + } return true; } function isRemoteAccessMetadata(value) { @@ -551,8 +901,12 @@ var IntercomClient = class extends EventEmitter { pendingSends = /* @__PURE__ */ new Map(); pendingLists = /* @__PURE__ */ new Map(); pendingAskControls = /* @__PURE__ */ new Map(); + pendingBossControls = /* @__PURE__ */ new Map(); outbox = null; + bossControlOutbox = null; remoteAccessCredential; + requestedBossRegistration; + _bossBinding; disconnecting = false; disconnectError = null; failPending(error2) { @@ -569,6 +923,11 @@ var IntercomClient = class extends EventEmitter { pending.resolve(false); } this.pendingAskControls.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + pending.reject(error2); + } + this.pendingBossControls.clear(); } get sessionId() { return this._sessionId; @@ -576,6 +935,12 @@ var IntercomClient = class extends EventEmitter { get outboxSize() { return this.outbox?.list().length ?? 0; } + get bossBinding() { + return this._bossBinding; + } + get bossControlOutboxSize() { + return this.bossControlOutbox?.list().length ?? 0; + } isConnected() { const socket = this.socket; return Boolean(socket && this._sessionId && !this.disconnecting && !socket.destroyed && !socket.writableEnded && socket.writable); @@ -597,6 +962,19 @@ var IntercomClient = class extends EventEmitter { if (this.socket) { return Promise.reject(new Error("Already connected")); } + try { + const canonicalSession = parseExactRegistrationFrame({ + type: "register", + ...typeof session === "object" && session !== null && !nodeUtilTypes2.isProxy(session) && Object.getOwnPropertyDescriptor(session, "boss") !== void 0 ? { registrationKind: "boss" } : {}, + protocol: INTERCOM_PROTOCOL_NAME, + version: INTERCOM_PROTOCOL_VERSION, + session + }).session; + this.requestedBossRegistration = session.boss === void 0 ? void 0 : parseBossParticipantRegistrationMetadata(session.boss); + if (canonicalSession !== session) throw new Error("Registration session identity changed during validation"); + } catch (error2) { + return Promise.reject(toError(error2)); + } return new Promise((resolve3, reject) => { let socket; let target; @@ -651,6 +1029,8 @@ var IntercomClient = class extends EventEmitter { this.socket = null; } this._sessionId = null; + this._bossBinding = void 0; + this.requestedBossRegistration = void 0; this.disconnectError = null; if (connectionEstablished && !wasDisconnecting) { this.emit("disconnected", disconnectError); @@ -696,6 +1076,7 @@ var IntercomClient = class extends EventEmitter { try { writeMessage(socket, { type: "register", + ...session.boss === void 0 ? {} : { registrationKind: "boss" }, protocol: INTERCOM_PROTOCOL_NAME, version: INTERCOM_PROTOCOL_VERSION, session, @@ -715,15 +1096,21 @@ var IntercomClient = class extends EventEmitter { }); } handleBrokerMessage(msg) { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes2.isProxy(msg)) { throw new Error("Invalid broker message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if (typeDescriptor === void 0 || !typeDescriptor.enumerable || !Object.hasOwn(typeDescriptor, "value") || typeof typeDescriptor.value !== "string") throw new Error("Invalid broker message"); const brokerMessage = msg; if (this._sessionId === null && brokerMessage.type !== "registered" && brokerMessage.type !== "error") { throw new Error(`Received ${brokerMessage.type} before registered`); } switch (brokerMessage.type) { case "registered": { + parseExactRegisteredFrame( + brokerMessage, + this.requestedBossRegistration === void 0 ? this.remoteAccessCredential === void 0 ? "ordinary-local" : "ordinary-remote" : "boss" + ); if (typeof brokerMessage.sessionId !== "string" || brokerMessage.protocol !== INTERCOM_PROTOCOL_NAME || brokerMessage.version !== INTERCOM_PROTOCOL_VERSION) { throw new Error("Invalid registered message"); } @@ -748,9 +1135,28 @@ var IntercomClient = class extends EventEmitter { } } } + if (this.requestedBossRegistration !== void 0) { + if (brokerMessage.remoteAccess !== void 0 || brokerMessage.access !== void 0) { + throw new Error("Boss registration returned folded remote-access metadata"); + } + const advertisement = parseBrokerCapabilityAdvertisement2(brokerMessage.capabilities); + if (!advertisement.features.some((feature) => feature.feature === BOSS_RUN_FEATURE2)) { + throw new Error("Broker did not echo the required boss-run-v1 feature contract"); + } + const binding = parseBossParticipantBindingMetadata(brokerMessage.boss, brokerMessage.sessionId); + const credential = this.requestedBossRegistration.credential; + if (binding.featureContract.feature !== this.requestedBossRegistration.featureContract.feature || binding.binding.bossRunId !== credential.bossRunId || binding.binding.participantId !== credential.participantId || binding.binding.role !== credential.role || binding.binding.communicationProfile !== credential.communicationProfile || binding.binding.bindingEpoch !== credential.bindingEpoch) { + throw new Error("Broker returned a Boss binding that does not match the authenticated registration request"); + } + this._bossBinding = binding; + } else if (brokerMessage.boss !== void 0) { + throw new Error("Broker attached unsolicited Boss binding metadata to an ordinary registration"); + } this._sessionId = brokerMessage.sessionId; this.outbox = new PersistentOutboundOutbox(brokerMessage.sessionId); + this.bossControlOutbox = this._bossBinding === void 0 ? null : new PersistentBossControlOutbox(brokerMessage.sessionId); this.replayOutbox(); + this.replayBossControlOutbox(); this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId }); break; } @@ -775,6 +1181,48 @@ var IntercomClient = class extends EventEmitter { this.emit("message", from, message, deliveryId); break; } + case "boss_control": { + const { deliveryId, from } = brokerMessage; + if (typeof deliveryId !== "string" || !isSessionInfo(from)) { + throw new Error("Invalid boss_control event"); + } + const envelope = bossControlKind(brokerMessage.envelope).envelope; + const source = from.boss === void 0 ? void 0 : parseBossParticipantBindingMetadata(from.boss, from.id).binding; + if (source === void 0 || source.state !== "active" || source.bossRunId !== envelope.bossRunId || source.participantId !== envelope.participantId || source.bindingEpoch !== envelope.bindingEpoch) throw new Error("Boss control event sender does not match its broker-owned binding"); + this.emit("boss_control", from, envelope, deliveryId); + break; + } + case "boss_control_result": { + const result = parseBossControlResult(brokerMessage); + const { requestId, messageId, idempotencyKey, deliveryId } = result; + const stored = this.bossControlOutbox?.find(idempotencyKey); + if (!stored || stored.envelope.messageId !== requestId) throw new Error("Boss control result does not match the durable outbox binding"); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control result correlation does not match the pending request"); + } + this.bossControlOutbox.removeCorrelated(idempotencyKey, messageId, deliveryId); + if (pending) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(requestId); + pending.resolve(result); + } + break; + } + case "boss_control_ack": { + const { requestId, messageId, idempotencyKey, deliveryId } = parseBossControlAck(brokerMessage); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control acknowledgement correlation does not match the pending request"); + } + const transition = this.bossControlOutbox?.markAccepted(idempotencyKey, messageId, deliveryId); + if (transition === void 0) throw new Error("Boss control acknowledgement has no durable outbox"); + if (pending?.deliveryId !== void 0 && pending.deliveryId !== deliveryId) { + throw new Error("Boss control acknowledgement changed the pending deliveryId"); + } + if (pending) pending.deliveryId = deliveryId; + break; + } case "delivery_accepted": { const { deliveryId, messageId } = brokerMessage; if (typeof deliveryId !== "string" || typeof messageId !== "string") { @@ -1025,6 +1473,64 @@ var IntercomClient = class extends EventEmitter { } }); } + sendBossControl(to, envelopeValue) { + let socket; + try { + socket = this.requireActiveSocket(); + } catch (error2) { + return Promise.reject(toError(error2)); + } + let envelope; + try { + envelope = bossControlKind(envelopeValue).envelope; + const binding = this._bossBinding?.binding; + if (binding === void 0 || binding.state !== "active" || envelope.bossRunId !== binding.bossRunId || envelope.participantId !== binding.participantId || envelope.bindingEpoch !== binding.bindingEpoch) throw new Error("Boss control envelope does not match this client's active participant binding"); + } catch (error2) { + return Promise.reject(toError(error2)); + } + const requestId = envelope.messageId; + if (this.pendingBossControls.has(requestId)) { + return Promise.resolve({ + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "rejected", + delivered: false, + code: "INVALID_CONTROL", + reason: "Boss requestId is already pending" + }); + } + try { + if (!this.bossControlOutbox) throw new Error("Durable Boss control outbox is unavailable"); + this.bossControlOutbox.enqueue(to, envelope); + } catch (error2) { + return Promise.reject(toError(error2)); + } + return new Promise((resolve3, reject) => { + const timeout = setTimeout(() => { + if (!this.pendingBossControls.delete(requestId)) return; + reject(new Error("Boss control delivery timeout")); + }, 1e4); + timeout.unref?.(); + this.pendingBossControls.set(requestId, { + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + resolve: resolve3, + reject, + timeout + }); + try { + writeMessage(socket, { type: "boss_control", requestId, to, envelope }); + } catch (error2) { + clearTimeout(timeout); + this.pendingBossControls.delete(requestId); + reject(toError(error2)); + } + }); + } + acknowledgeBossControl(deliveryId, messageId, idempotencyKey) { + return this.writeControlMessage({ type: "boss_control_received", deliveryId, messageId, idempotencyKey }); + } acknowledgeMessage(deliveryId) { return this.writeControlMessage({ type: "message_received", deliveryId }); } @@ -1080,6 +1586,22 @@ var IntercomClient = class extends EventEmitter { } } } + replayBossControlOutbox() { + const socket = this.socket; + if (!socket || socket.destroyed || !this._sessionId || !this.bossControlOutbox) return; + for (const entry of this.bossControlOutbox.list()) { + try { + writeMessage(socket, { + type: "boss_control", + requestId: entry.envelope.messageId, + to: entry.to, + envelope: entry.envelope + }); + } catch { + return; + } + } + } updatePresence(updates) { if (this.disconnecting) { return; @@ -1094,38 +1616,39 @@ var IntercomClient = class extends EventEmitter { // broker/spawn.ts import { spawn } from "child_process"; -import { existsSync as existsSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs"; -import { join as join3, dirname as dirname2 } from "path"; +import { existsSync as existsSync3, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2 } from "fs"; +import { join as join4, dirname as dirname2 } from "path"; import { fileURLToPath } from "url"; import { createRequire } from "module"; import net2 from "net"; import { randomUUID as randomUUID3 } from "crypto"; +import { POLICY_SEMANTICS_HASH as POLICY_SEMANTICS_HASH2, POLICY_SEMANTICS_VERSION as POLICY_SEMANTICS_VERSION2 } from "@dataforxyz/agent-intercom-core"; var INTERCOM_DIR = getIntercomDirPath(); -var EXTENSION_DIR = join3(dirname2(fileURLToPath(import.meta.url)), ".."); -var BROKER_PID = join3(INTERCOM_DIR, "broker.pid"); -var BROKER_SPAWN_LOCK = join3(INTERCOM_DIR, "broker.spawn.lock"); +var EXTENSION_DIR = join4(dirname2(fileURLToPath(import.meta.url)), ".."); +var BROKER_PID = join4(INTERCOM_DIR, "broker.pid"); +var BROKER_SPAWN_LOCK = join4(INTERCOM_DIR, "broker.spawn.lock"); function sleep(ms) { return new Promise((resolve3) => setTimeout(resolve3, ms)); } function getBrokerEntryPath(moduleUrl = import.meta.url) { const moduleDir = dirname2(fileURLToPath(moduleUrl)); - const bundledBroker = join3(moduleDir, "broker.mjs"); - return existsSync2(bundledBroker) ? bundledBroker : join3(moduleDir, "broker.ts"); + const bundledBroker = join4(moduleDir, "broker.mjs"); + return existsSync3(bundledBroker) ? bundledBroker : join4(moduleDir, "broker.ts"); } function getTsxCliPath(extensionDir = EXTENSION_DIR) { try { const requireFromExtension = createRequire(import.meta.url); const tsxMain = requireFromExtension.resolve("tsx"); - return join3(dirname2(tsxMain), "cli.mjs"); + return join4(dirname2(tsxMain), "cli.mjs"); } catch { - return join3(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); + return join4(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); } } function quoteWindowsArg(value) { return `"${value.replace(/"/g, '""')}"`; } function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) { - return join3(intercomDir, "broker-launch.vbs"); + return join4(intercomDir, "broker-launch.vbs"); } function usesDefaultBrokerCommand(brokerCommand, brokerArgs) { return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx"; @@ -1153,7 +1676,7 @@ function isBrokerHealthOkMessage(message, requestId) { const remoteAccess = response.remoteAccess; if (typeof remoteAccess !== "object" || remoteAccess === null || Array.isArray(remoteAccess)) return false; const contract = remoteAccess; - return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION && contract.policySemanticsHash === POLICY_SEMANTICS_HASH; + return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION2 && contract.policySemanticsHash === POLICY_SEMANTICS_HASH2; } function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) { ensureIntercomRuntimeDir(dirname2(launcherPath)); @@ -1259,10 +1782,10 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs) { } } async function stopBrokerProcess(pidFile = BROKER_PID, timeoutMs = 3e3) { - if (!existsSync2(pidFile)) return; + if (!existsSync3(pidFile)) return; let pid; try { - pid = Number.parseInt(readFileSync4(pidFile, "utf-8").trim(), 10); + pid = Number.parseInt(readFileSync5(pidFile, "utf-8").trim(), 10); } catch { return; } @@ -1287,9 +1810,9 @@ async function isBrokerRunning() { if (await checkSocketConnectable()) { return true; } - if (!existsSync2(BROKER_PID)) return false; + if (!existsSync3(BROKER_PID)) return false; try { - const pid = parseInt(readFileSync4(BROKER_PID, "utf-8").trim(), 10); + const pid = parseInt(readFileSync5(BROKER_PID, "utf-8").trim(), 10); if (!Number.isFinite(pid)) return false; process.kill(pid, 0); return checkSocketConnectable(); @@ -1386,11 +1909,11 @@ ${Date.now()} return false; } function isSpawnLockStale() { - if (!existsSync2(BROKER_SPAWN_LOCK)) { + if (!existsSync3(BROKER_SPAWN_LOCK)) { return false; } try { - const [pidLine = "", createdAtLine = "0"] = readFileSync4(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); + const [pidLine = "", createdAtLine = "0"] = readFileSync5(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); const pid = Number.parseInt(pidLine, 10); const createdAt = Number.parseInt(createdAtLine, 10); const ageMs = Date.now() - createdAt; @@ -1424,8 +1947,8 @@ async function waitForBroker(timeoutMs = 5e3) { } // config.ts -import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs"; -import { join as join4, resolve as resolve2 } from "path"; +import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs"; +import { join as join5, resolve as resolve2 } from "path"; import { homedir as homedir2 } from "os"; var DEFAULT_ASK_TIMEOUT_MS = 45 * 1e3; var MAX_ASK_TIMEOUT_MS = 120 * 1e3; @@ -1447,8 +1970,8 @@ function getAskTimeoutMs() { return validateAskTimeoutMs(value, "PI_INTERCOM_ASK_TIMEOUT_MS"); } function getConfigPath() { - const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join4(homedir2(), ".pi", "agent"); - return join4(agentDir, "intercom", "config.json"); + const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join5(homedir2(), ".pi", "agent"); + return join5(agentDir, "intercom", "config.json"); } var defaults = { brokerCommand: "npx", @@ -1465,11 +1988,11 @@ var defaults = { }; function loadConfig() { const configPath = getConfigPath(); - if (!existsSync3(configPath)) { + if (!existsSync4(configPath)) { return { ...defaults }; } try { - const raw = readFileSync5(configPath, "utf-8"); + const raw = readFileSync6(configPath, "utf-8"); const parsed = JSON.parse(raw); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error("Config must be a JSON object"); @@ -1563,39 +2086,126 @@ function loadConfig() { // codex/team.ts import { readFile } from "node:fs/promises"; -import { join as join5 } from "node:path"; -var LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +import { join as join6 } from "node:path"; +import { + BOSS_PARTICIPANT_ROLES, + parseParticipantState as parseParticipantState2, + parseWorkerIdentityV2 as parseWorkerIdentityV22, + workerIdentityFromEnvironment +} from "@dataforxyz/agent-intercom-core/boss"; +var LEGACY_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +var CANONICAL_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "registering", "ready", "working", "waiting", "paused", "stalled", "blocked", "unreachable"]); var stringValue = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0; var connectedTo = (sessions, target) => { const normalized = target.toLowerCase(); return sessions.some((session) => session.id === target || session.name?.toLowerCase() === normalized); }; +function bossIdentityFromEnvironment(env) { + const bossKeys = ["AGENT_INTERCOM_BOSS_RUN_ID", "AGENT_INTERCOM_PARTICIPANT_ID", "AGENT_INTERCOM_BINDING_EPOCH"]; + if (!bossKeys.some((key) => env[key] !== void 0)) return void 0; + const identity = workerIdentityFromEnvironment(env); + if (!("bossRunId" in identity)) throw new Error("Incomplete Boss worker identity cannot discover a team"); + return identity; +} +function canonicalWorker(value) { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("worker must be an object"); + const worker = value; + const identity = parseWorkerIdentityV22({ + version: "orc.worker-identity.v2", + workerId: worker.id, + workerIncarnationId: worker.workerIncarnationId, + workerGeneration: worker.workerGeneration, + ...worker.bossRunId === void 0 ? {} : { bossRunId: worker.bossRunId }, + ...worker.participantId === void 0 ? {} : { participantId: worker.participantId }, + ...worker.bindingEpoch === void 0 ? {} : { bindingEpoch: worker.bindingEpoch } + }); + parseParticipantState2(worker.state, "$.worker.state"); + if (typeof worker.role !== "string" || !BOSS_PARTICIPANT_ROLES.includes(worker.role)) { + throw new Error("worker role is not canonical"); + } + if (worker.owned !== true || !stringValue(worker.managerSessionId) || !stringValue(worker.intercomTarget)) { + throw new Error("canonical worker ownership routing is incomplete"); + } + return { ...worker, canonicalIdentity: identity }; +} +function exactBossRosterSession(sessions, worker) { + const identity = worker.canonicalIdentity; + const target = stringValue(worker.intercomTarget); + const role = stringValue(worker.role); + const state = stringValue(worker.state); + if (!identity || !("bossRunId" in identity) || !target || !role || !state) return void 0; + const matches = sessions.filter((candidate) => candidate.id === target); + if (matches.length !== 1) return void 0; + const [session] = matches; + if (!session?.boss?.binding || session.boss.workerIdentity === void 0 || session.boss.participantState === void 0) return void 0; + try { + const sessionIdentity = parseWorkerIdentityV22(session.boss.workerIdentity); + const sessionState = parseParticipantState2(session.boss.participantState, "$.session.boss.participantState"); + const binding = session.boss.binding; + return "bossRunId" in sessionIdentity && session.id === target && binding.sessionId === session.id && binding.state === "active" && binding.bossRunId === identity.bossRunId && binding.participantId === identity.participantId && binding.bindingEpoch === identity.bindingEpoch && binding.role === role && sessionIdentity.workerId === identity.workerId && sessionIdentity.workerIncarnationId === identity.workerIncarnationId && sessionIdentity.workerGeneration === identity.workerGeneration && sessionIdentity.bossRunId === identity.bossRunId && sessionIdentity.participantId === identity.participantId && sessionIdentity.bindingEpoch === identity.bindingEpoch && sessionState === state ? session : void 0; + } catch { + return void 0; + } +} async function readWorkers(agentDir) { try { - const parsed = JSON.parse(await readFile(join5(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); - return Array.isArray(parsed.workers) ? parsed.workers : []; + const parsed = JSON.parse(await readFile(join6(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("worker snapshot must be an object"); + const snapshot = parsed; + if (snapshot.version !== 1 && snapshot.version !== 2 || !Array.isArray(snapshot.workers)) throw new Error("unsupported worker snapshot version"); + if (snapshot.version === 1) return { version: 1, workers: snapshot.workers }; + return { version: 2, workers: snapshot.workers.map(canonicalWorker) }; } catch { - return []; + return { version: 1, workers: [] }; } } async function resolveIntercomTeam(input) { const env = input.env ?? process.env; - const workers = await readWorkers(input.agentDir ?? getAgentDirPath()); + const snapshot = await readWorkers(input.agentDir ?? getAgentDirPath()); + const workers = snapshot.workers; const workerId = stringValue(env.AGENT_INTERCOM_WORKER_ID); + const bossIdentity = bossIdentityFromEnvironment(env); const runId = stringValue(env.AGENT_INTERCOM_RUN_ID); - const current = workerId ? workers.find((worker) => stringValue(worker.id) === workerId && (!runId || stringValue(worker.runId) === runId)) : void 0; - const managerTarget = stringValue(current?.managerSessionId) ?? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID); + const currentMatches = workerId ? workers.filter((worker) => stringValue(worker.id) === workerId && (bossIdentity === void 0 ? !runId || stringValue(worker.runId) === runId : snapshot.version === 2 && worker.canonicalIdentity?.workerId === bossIdentity.workerId && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId && worker.canonicalIdentity.participantId === bossIdentity.participantId && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch)) : []; + const current = bossIdentity === void 0 ? currentMatches[0] : currentMatches.length === 1 ? currentMatches[0] : void 0; + const currentTarget = stringValue(current?.intercomTarget); + const exactCurrentProjection = current !== void 0 && currentTarget === input.selfId && workers.filter((worker) => stringValue(worker.id) === workerId).length === 1 && workers.filter((worker) => stringValue(worker.intercomTarget) === currentTarget).length === 1 && exactBossRosterSession(input.sessions, current) !== void 0; + if (bossIdentity !== void 0 && !exactCurrentProjection) { + return { self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false }, coworkers: [] }; + } + const managerTarget = stringValue(current?.managerSessionId) ?? (bossIdentity === void 0 ? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID) : void 0); const teamId = managerTarget ?? input.selfId; - const coworkers = workers.filter((worker) => worker.owned === true).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { + const currentRole = stringValue(current?.role); + const canDiscoverOwnedRoster = bossIdentity === void 0 || currentRole === "manager" || currentRole === "controller"; + const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true).filter((worker) => bossIdentity === void 0 || snapshot.version === 2 && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => stringValue(worker.intercomTarget) !== managerTarget).filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { const id = stringValue(worker.id); if (!id) return void 0; const target = stringValue(worker.intercomTarget) ?? id; - return { id, target, ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, connected: connectedTo(input.sessions, target) }; + const connected = bossIdentity === void 0 ? connectedTo(input.sessions, target) : exactBossRosterSession(input.sessions, worker) !== void 0; + if (!connected) return void 0; + return { + id, + target, + ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, + ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, + ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, + connected + }; }).filter((member) => Boolean(member)); - return { teamId, self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: !managerTarget }, manager: managerTarget ? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } : { target: input.selfId, connected: true }, coworkers }; + const managerWorker = managerTarget === void 0 ? void 0 : workers.find((worker) => stringValue(worker.intercomTarget) === managerTarget && (bossIdentity === void 0 || snapshot.version === 2 && stringValue(worker.role) === "manager" && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId)); + const managerConnected = managerTarget === void 0 ? true : bossIdentity === void 0 ? connectedTo(input.sessions, managerTarget) : managerWorker !== void 0 && exactBossRosterSession(input.sessions, managerWorker) !== void 0; + return { + teamId, + self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: bossIdentity === void 0 && !managerTarget }, + ...managerTarget ? { manager: { target: managerTarget, connected: managerConnected } } : bossIdentity === void 0 ? { manager: { target: input.selfId, connected: true } } : {}, + coworkers + }; } function formatIntercomTeam(team) { - const lines = [`Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}`]; + const lines = [ + `Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, + `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}` + ]; if (!team.coworkers.length) lines.push("Coworkers: none"); else { lines.push("Coworkers:"); @@ -1653,7 +2263,7 @@ function publicPendingEntry(entry, selector) { }; } function shortHash(value) { - return createHash2("sha256").update(value).digest("hex").slice(0, 8); + return createHash3("sha256").update(value).digest("hex").slice(0, 8); } function buildCodexRuntimeIdentity(env = process.env, cwd = env.PWD || processCwd(), pid = process.pid) { const sessionId = env.CODEX_INTERCOM_SESSION_ID?.trim() || env.CODEX_PEER_ID?.trim() || `codex-${pid}-${shortHash(cwd)}`; @@ -1924,12 +2534,12 @@ Pending asks: ${this.unresolvedAsks.size}`, } ); } - async list(scope = "machine", includeSelf = false) { + async list(scope2 = "machine", includeSelf = false) { const client = await this.connect(); let sessions = await client.listSessions(); - if (scope === "directory") { + if (scope2 === "directory") { sessions = sessions.filter((session) => session.cwd === this.identity.cwd); - } else if (scope === "repo") { + } else if (scope2 === "repo") { const currentRoot = detectGitRoot(this.identity.cwd); sessions = currentRoot ? sessions.filter((session) => detectGitRoot(session.cwd) === currentRoot) : []; } diff --git a/dist/coi.mjs b/dist/coi.mjs index 27f7380..bb95395 100755 --- a/dist/coi.mjs +++ b/dist/coi.mjs @@ -1,13 +1,14 @@ #!/usr/bin/env node -process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=coi sourceSha256=28cbe04c291ec9ca89e519b437e41d7a7c359f85cf99d2fcf3e6cb0f74dcee2c\n"); +process.stderr.write("[agent-intercom-build] package=@dataforxyz/agent-intercom-codex version=0.10.0 target=coi sourceSha256=e3924d8a81ca3579d920e6938f77fe75f36c8eeb02d45b49b2d940b7a67b6410\n"); // codex/coi.ts import { once as once2 } from "node:events"; import { spawn as spawn4, spawnSync as spawnSync2 } from "node:child_process"; -import { createHash as createHash3 } from "node:crypto"; -import { existsSync as existsSync5, readdirSync, rmSync, statSync } from "node:fs"; -import { basename as basename2, join as join7, resolve as resolve4 } from "node:path"; +import { createHash as createHash4 } from "node:crypto"; +import { existsSync as existsSync6, readdirSync, rmSync, statSync } from "node:fs"; +import { basename as basename2, join as join8, resolve as resolve4 } from "node:path"; import { setTimeout as delay2 } from "node:timers/promises"; +import { types as nodeUtilTypes4 } from "node:util"; // codex/bridge-daemon.ts import { once } from "node:events"; @@ -21,6 +22,27 @@ import { randomBytes, createHash } from "node:crypto"; import net from "node:net"; import readline from "node:readline"; import { setTimeout as delay } from "node:timers/promises"; + +// codex/boss-client.ts +var HARDENED_BOSS_CODEX_DEFAULTS = Object.freeze({ + boss_participant: Object.freeze({ approvalPolicy: "untrusted", sandbox: "workspace-write" }), + boss_reviewer: Object.freeze({ approvalPolicy: "untrusted", sandbox: "read-only" }) +}); +var PROVIDER_AUTHORITY_UNAVAILABLE = "PROVIDER_AUTHORITY_UNAVAILABLE"; +var ProviderAuthorityUnavailableError = class extends Error { + constructor(bossClient) { + super(`${PROVIDER_AUTHORITY_UNAVAILABLE}: ${bossClient} requires a broker-owned, artifact-attested Codex provider executable`); + this.bossClient = bossClient; + this.name = "ProviderAuthorityUnavailableError"; + } + bossClient; + code = PROVIDER_AUTHORITY_UNAVAILABLE; +}; +function assertHardenedBossProviderAuthority(bossClient) { + if (bossClient !== void 0) throw new ProviderAuthorityUnavailableError(bossClient); +} + +// codex/app-server-client.ts var DEFAULT_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3; var MAX_WEBSOCKET_MESSAGE_BYTES = 16 * 1024 * 1024; var UNIX_WEBSOCKET_CONNECT_TIMEOUT_MS = 1e4; @@ -57,8 +79,9 @@ var CodexAppServerClient = class extends EventEmitter { pending = /* @__PURE__ */ new Map(); initialized = false; options; - constructor(options = {}) { + constructor(options = {}, protectedBossClient) { super(); + assertHardenedBossProviderAuthority(protectedBossClient); this.options = { command: options.command ?? "codex", args: options.args ?? ["app-server"], @@ -68,7 +91,8 @@ var CodexAppServerClient = class extends EventEmitter { startDaemon: options.startDaemon ?? false, startDaemonCommand: options.startDaemonCommand ?? "codex", startDaemonArgs: options.startDaemonArgs ?? ["app-server", "daemon", "start"], - requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS + requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, + env: options.env ?? process.env }; } setServerRequestHandler(handler) { @@ -79,6 +103,7 @@ var CodexAppServerClient = class extends EventEmitter { if (this.options.startDaemon) { const started = spawnSync(this.options.startDaemonCommand, this.options.startDaemonArgs, { encoding: "utf8", + env: this.options.env, stdio: ["ignore", "pipe", "pipe"] }); if (started.status !== 0) { @@ -92,7 +117,7 @@ var CodexAppServerClient = class extends EventEmitter { } const proc = spawn(this.options.command, this.options.args, { stdio: ["pipe", "pipe", "pipe"], - env: process.env + env: this.options.env }); this.proc = proc; this.rl = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity }); @@ -482,8 +507,9 @@ var WebSocketFrameDecoder = class { // codex/bridge-config.ts import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs"; -import { dirname, join as join2, resolve as resolve2 } from "node:path"; +import { dirname, join as join2, parse as parsePath, resolve as resolve2 } from "node:path"; import { cwd as processCwd } from "node:process"; +import { types as nodeUtilTypes2 } from "node:util"; // broker/paths.ts import { chmodSync, mkdirSync, readFileSync } from "fs"; @@ -555,11 +581,314 @@ function restrictIntercomRuntimeFile(filePath, platform = process.platform) { } } +// broker/boss-adapter.ts +import { + BOSS_CAPABILITY_FEATURE_DIGEST, + BOSS_CONTROL_ENVELOPE_VERSION, + BOSS_POLICY_PRINCIPAL_VERSION, + BOSS_POLICY_SEMANTICS_HASH, + BOSS_RUN_FEATURE, + BOSS_RUN_FEATURE_CONTRACT, + BOSS_RUN_FEATURE_SEMANTICS_HASH, + BOSS_RUN_FEATURE_VERSION, + BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + BROKER_FEATURE_ATTESTATION_VERSION, + INTERCOM_BASE_PROTOCOL_VERSION, + authorizeFeatureAware, + brokerFeatureSetHash, + parseBossControlEnvelope, + parseBossParticipantBinding, + parseBossParticipantCredentialEnvelope, + parseBossRunFeatureContract, + parseBrokerCapabilityAdvertisement, + parseParticipantState, + parseWorkerIdentityV2 +} from "@dataforxyz/agent-intercom-core/boss"; +import { + ContractValidationError, + assertExactKeys, + assertRecord, + canonicalJson +} from "@dataforxyz/agent-intercom-core/canonical"; +import { types as nodeUtilTypes } from "node:util"; +var BOSS_ADVERTISEMENT_PREDICATES = [ + "protectedProvider", + "brokerIdentity", + "credentialRegistry", + "authorityTransitions", + "participantHealth" +]; +var DORMANT_BOSS_ADVERTISEMENT_READINESS = Object.freeze({ + protectedProvider: false, + brokerIdentity: false, + credentialRegistry: false, + authorityTransitions: false, + participantHealth: false +}); +var ORDINARY_SESSION_REGISTRATION_KEYS = [ + "cwd", + "model", + "pid", + "startedAt", + "lastActivity" +]; +var OPTIONAL_SESSION_REGISTRATION_KEYS = ["name", "status", "runtimeInstanceId"]; +function assertBossCanonicalData(value, path = "$", seen = /* @__PURE__ */ new WeakSet()) { + if (typeof value !== "object" || value === null) return; + if (nodeUtilTypes.isProxy(value)) { + throw new ContractValidationError(path, "proxies are not supported"); + } + if (seen.has(value)) throw new ContractValidationError(path, "cyclic values are not supported"); + seen.add(value); + if (Array.isArray(value)) { + if (Object.getPrototypeOf(value) !== Array.prototype) { + throw new ContractValidationError(path, "must use the exact Array prototype"); + } + const ownKeys = Reflect.ownKeys(value); + const expectedKeys = /* @__PURE__ */ new Set(["length"]); + for (let index = 0; index < value.length; index += 1) expectedKeys.add(String(index)); + if (ownKeys.length !== expectedKeys.size || ownKeys.some((key) => !expectedKeys.has(key))) { + throw new ContractValidationError(path, "must be a dense array without symbols or extra properties"); + } + for (let index = 0; index < value.length; index += 1) { + if (!Object.hasOwn(value, index)) { + throw new ContractValidationError(`${path}[${index}]`, "sparse array holes are not supported"); + } + const descriptor = Object.getOwnPropertyDescriptor(value, String(index)); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}[${index}]`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}[${index}]`, seen); + } + return; + } + if (Object.getPrototypeOf(value) !== Object.prototype) { + throw new ContractValidationError(path, "must use the exact Object prototype"); + } + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") throw new ContractValidationError(path, "symbol properties are not supported"); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0 || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`${path}.${key}`, "must be an own enumerable data property"); + } + assertBossCanonicalData(descriptor.value, `${path}.${key}`, seen); + } +} +function parseBossAdvertisementReadiness(value) { + assertBossCanonicalData(value, "$.readiness"); + assertRecord(value); + assertExactKeys(value, BOSS_ADVERTISEMENT_PREDICATES); + const parsed = {}; + for (const predicate of BOSS_ADVERTISEMENT_PREDICATES) { + const enabled = ownDataValue(value, predicate); + if (typeof enabled !== "boolean") { + throw new ContractValidationError(`$.readiness.${predicate}`, "must be a boolean"); + } + parsed[predicate] = enabled; + } + return parsed; +} +function missingBossAdvertisementPredicates(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + const parsed = parseBossAdvertisementReadiness(readiness); + return BOSS_ADVERTISEMENT_PREDICATES.filter((predicate) => parsed[predicate] !== true); +} +function bossCapabilityAdvertisement(readiness = DORMANT_BOSS_ADVERTISEMENT_READINESS) { + if (missingBossAdvertisementPredicates(readiness).length > 0) return void 0; + const features = [{ + version: BROKER_FEATURE_ATTESTATION_VERSION, + feature: BOSS_RUN_FEATURE, + featureVersion: BOSS_RUN_FEATURE_VERSION, + semanticsHash: BOSS_RUN_FEATURE_SEMANTICS_HASH, + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }]; + return parseBrokerCapabilityAdvertisement({ + baseProtocolVersion: INTERCOM_BASE_PROTOCOL_VERSION, + features, + protocolFeatureContractHash: BOSS_RUN_PROTOCOL_FEATURE_CONTRACT_HASH, + featureSetHash: brokerFeatureSetHash(features), + controlEnvelopeVersion: BOSS_CONTROL_ENVELOPE_VERSION, + capabilityDigest: BOSS_CAPABILITY_FEATURE_DIGEST + }); +} +function optionalOwnDataValue(value, key) { + assertRecord(value); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === void 0) return void 0; + if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) { + throw new ContractValidationError(`$.${key}`, "must be an own enumerable data property"); + } + return descriptor.value; +} +function ownDataValue(value, key) { + const result = optionalOwnDataValue(value, key); + if (result === void 0) throw new ContractValidationError(`$.${key}`, "is required"); + return result; +} +function parseBossParticipantRegistrationMetadata(value) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys(value, ["featureContract", "credential"]); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly negotiate boss-run-v1 over base protocol v3"); + } + const credential = parseBossParticipantCredentialEnvelope(ownDataValue(value, "credential")); + if (credential.namespace !== featureContract.feature) { + throw new ContractValidationError("$.credential.namespace", "must match the negotiated feature namespace"); + } + return { featureContract, credential }; +} +function exactRegistrationKind(session, value) { + assertBossCanonicalData(session, "$.session"); + assertRecord(session); + const boss = optionalOwnDataValue(session, "boss"); + if (boss === void 0) { + if (value === void 0) return "ordinary"; + throw new ContractValidationError("$.registrationKind", "must be absent when Boss metadata is absent"); + } + if (value !== "boss") { + throw new ContractValidationError("$.registrationKind", "must be boss when Boss metadata is present"); + } + return "boss"; +} +function parseExactRegistrationFrame(value) { + assertBossCanonicalData(value, "$.register"); + assertRecord(value); + const session = ownDataValue(value, "session"); + assertRecord(session); + const registrationKind = optionalOwnDataValue(value, "registrationKind"); + const kind = exactRegistrationKind(session, registrationKind); + if (kind === "ordinary") { + assertExactKeys(value, ["type", "protocol", "version", "session"], ["sessionId", "stateId", "access"]); + assertExactKeys(session, ORDINARY_SESSION_REGISTRATION_KEYS, OPTIONAL_SESSION_REGISTRATION_KEYS); + } else { + assertExactKeys(value, ["type", "registrationKind", "protocol", "version", "session"], ["sessionId", "stateId"]); + assertExactKeys(session, [...ORDINARY_SESSION_REGISTRATION_KEYS, "boss"], OPTIONAL_SESSION_REGISTRATION_KEYS); + parseBossParticipantRegistrationMetadata(ownDataValue(session, "boss")); + } + if (ownDataValue(value, "type") !== "register") { + throw new ContractValidationError("$.register.type", "must be register"); + } + return value; +} +function parseExactRegisteredFrame(value, expected) { + assertBossCanonicalData(value, "$.registered"); + assertRecord(value); + if (expected === "boss") { + assertExactKeys(value, ["type", "registrationKind", "sessionId", "protocol", "version", "capabilities", "boss"]); + if (ownDataValue(value, "registrationKind") !== "boss") { + throw new ContractValidationError("$.registered.registrationKind", "must be boss"); + } + const sessionId = ownDataValue(value, "sessionId"); + if (typeof sessionId !== "string" || sessionId.length === 0) { + throw new ContractValidationError("$.registered.sessionId", "must be a non-empty string"); + } + const advertisement = parseBrokerCapabilityAdvertisement(ownDataValue(value, "capabilities")); + const expectedAdvertisement = bossCapabilityAdvertisement({ + protectedProvider: true, + brokerIdentity: true, + credentialRegistry: true, + authorityTransitions: true, + participantHealth: true + }); + const bossFeature = advertisement.features.find((feature) => feature.feature === BOSS_RUN_FEATURE); + if (bossFeature === void 0 || canonicalJson(bossFeature) !== canonicalJson(expectedAdvertisement.features[0]) || advertisement.baseProtocolVersion !== expectedAdvertisement.baseProtocolVersion || advertisement.protocolFeatureContractHash !== expectedAdvertisement.protocolFeatureContractHash || advertisement.controlEnvelopeVersion !== expectedAdvertisement.controlEnvelopeVersion || advertisement.capabilityDigest !== expectedAdvertisement.capabilityDigest) throw new ContractValidationError("$.registered.capabilities", "must exactly echo the requested boss-run-v1 contract"); + parseBossParticipantBindingMetadata(ownDataValue(value, "boss"), sessionId); + } else if (expected === "ordinary-remote") { + assertExactKeys(value, ["type", "sessionId", "protocol", "version", "remoteAccess", "access"]); + } else { + assertExactKeys(value, ["type", "sessionId", "protocol", "version"]); + } + if (ownDataValue(value, "type") !== "registered") { + throw new ContractValidationError("$.registered.type", "must be registered"); + } + return value; +} +function parseBossParticipantBindingMetadata(value, expectedSessionId) { + assertBossCanonicalData(value); + assertRecord(value); + assertExactKeys( + value, + ["featureContract", "binding", "brokerIdentityVerified"], + ["assignedParticipantIds", "requestingPrincipalId", "workerIdentity", "participantState"] + ); + const featureContract = parseBossRunFeatureContract(ownDataValue(value, "featureContract")); + if (featureContract.baseProtocolVersion !== INTERCOM_BASE_PROTOCOL_VERSION || canonicalJson(featureContract) !== canonicalJson(BOSS_RUN_FEATURE_CONTRACT)) { + throw new ContractValidationError("$.featureContract", "must exactly bind boss-run-v1 over base protocol v3"); + } + const binding = parseBossParticipantBinding(ownDataValue(value, "binding")); + if (ownDataValue(value, "brokerIdentityVerified") !== true) { + throw new ContractValidationError("$.brokerIdentityVerified", "must be true for a broker-owned Boss binding"); + } + if (expectedSessionId !== void 0 && binding.sessionId !== expectedSessionId) { + throw new ContractValidationError("$.binding.sessionId", "must match the registered intercom session"); + } + const rawAssignedParticipantIds = optionalOwnDataValue(value, "assignedParticipantIds"); + let assignedParticipantIds; + if (rawAssignedParticipantIds !== void 0) { + if (binding.role !== "manager" || !Array.isArray(rawAssignedParticipantIds) || rawAssignedParticipantIds.some((entry) => typeof entry !== "string" || entry.length === 0) || new Set(rawAssignedParticipantIds).size !== rawAssignedParticipantIds.length) { + throw new ContractValidationError("$.assignedParticipantIds", "must be a unique participant list present only for a Manager"); + } + assignedParticipantIds = rawAssignedParticipantIds; + } + if (binding.role === "manager" && assignedParticipantIds === void 0) { + throw new ContractValidationError("$.assignedParticipantIds", "is required for a Manager policy binding"); + } + const rawRequestingPrincipalId = optionalOwnDataValue(value, "requestingPrincipalId"); + if (binding.role === "council" !== (typeof rawRequestingPrincipalId === "string" && rawRequestingPrincipalId.length > 0)) { + throw new ContractValidationError("$.requestingPrincipalId", "is required exactly for a Council policy binding"); + } + const requestingPrincipalId = typeof rawRequestingPrincipalId === "string" ? rawRequestingPrincipalId : void 0; + const rawWorkerIdentity = optionalOwnDataValue(value, "workerIdentity"); + const rawParticipantState = optionalOwnDataValue(value, "participantState"); + if (rawWorkerIdentity === void 0 !== (rawParticipantState === void 0)) { + throw new ContractValidationError("$.workerIdentity", "workerIdentity and participantState must be supplied together"); + } + const workerIdentity = rawWorkerIdentity === void 0 ? void 0 : parseWorkerIdentityV2(rawWorkerIdentity); + const participantState = rawParticipantState === void 0 ? void 0 : parseParticipantState(rawParticipantState, "$.participantState"); + if (workerIdentity !== void 0 && (!("bossRunId" in workerIdentity) || workerIdentity.bossRunId !== binding.bossRunId || workerIdentity.participantId !== binding.participantId || workerIdentity.bindingEpoch !== binding.bindingEpoch)) throw new ContractValidationError("$.workerIdentity", "must match the broker-owned participant binding"); + return { + featureContract, + binding, + brokerIdentityVerified: true, + ...assignedParticipantIds === void 0 ? {} : { assignedParticipantIds: [...assignedParticipantIds] }, + ...requestingPrincipalId === void 0 ? {} : { requestingPrincipalId }, + ...workerIdentity === void 0 ? {} : { workerIdentity, participantState } + }; +} +var BOSS_CONTROL_KIND_BY_TYPE = { + "boss.assignment.created": "assignment_request", + "boss.assignment.accepted": "assignment_response", + "boss.assignment.checkpoint": "assignment_response", + "boss.assignment.submitted": "assignment_response", + "boss.assignment.rejected": "assignment_response", + "boss.assignment.cancelled": "lifecycle", + "boss.staffing.requested": "staffing", + "boss.staffing.resolved": "staffing", + "boss.review.requested": "review_request", + "boss.review.submitted": "review_result", + "boss.council.requested": "review_request", + "boss.council.submitted": "review_result", + "boss.proof.submitted": "proof", + "boss.worker.health": "health", + "boss.worker.blocked": "health", + "boss.worker.failed": "health", + "boss.worker.notice": "lifecycle", + "boss.worker.notice_delivery_failed": "lifecycle", + "boss.decision.required": "decision" +}; +function bossControlKind(envelopeValue) { + assertBossCanonicalData(envelopeValue); + const envelope = parseBossControlEnvelope(envelopeValue); + return { envelope, controlKind: BOSS_CONTROL_KIND_BY_TYPE[envelope.type] }; +} + // codex/bridge-config.ts var DEFAULT_BRIDGE_CONFIG_PATH = join2(getIntercomDirPath(), "codex-bridge.json"); var DEFAULT_BRIDGE_STATE_PATH = join2(getIntercomDirPath(), "codex-bridge-state.json"); function isRecord(value) { - return typeof value === "object" && value !== null && !Array.isArray(value); + return typeof value === "object" && value !== null && !Array.isArray(value) && !nodeUtilTypes2.isProxy(value); } function optionalString(value, field) { if (value === void 0 || value === null) return void 0; @@ -572,11 +901,126 @@ function requireString(value, field) { if (!result) throw new Error(`${field} must be a non-empty string`); return result; } +function parseHardenedBossClientKind(value, field) { + if (value === void 0 || value === null) return void 0; + if (value === "boss_participant" || value === "boss_reviewer") return value; + throw new Error(`${field} must be boss_participant or boss_reviewer`); +} +function sandboxType(value) { + if (!isRecord(value)) return void 0; + return typeof value.type === "string" ? value.type : void 0; +} +function assertHardenedBossAgentConfig(agent) { + if (nodeUtilTypes2.isProxy(agent)) throw new Error("Hardened Boss agent config must not be a proxy"); + assertBossCanonicalData(agent, "$.agent"); + if (agent.bossClient === void 0) return; + if (agent.sandboxPolicy !== void 0 && !isRecord(agent.sandboxPolicy)) { + throw new Error(`${agent.bossClient} sandboxPolicy must be a plain object`); + } + if (agent.approvalPolicy !== void 0 && typeof agent.approvalPolicy !== "string") { + throw new Error(`${agent.bossClient} approvalPolicy must be a string`); + } + const type = sandboxType(agent.sandboxPolicy); + if (type === "dangerFullAccess" || type === "danger-full-access") { + throw new Error(`${agent.bossClient} cannot use danger-full-access`); + } + if (agent.approvalPolicy === "never") { + throw new Error(`${agent.bossClient} cannot disable approval checks`); + } + if (agent.bossClient === "boss_reviewer" && type !== void 0 && type !== "readOnly" && type !== "read-only") { + throw new Error("boss_reviewer must use a read-only sandbox"); + } + const canonicalCwd = resolve2(agent.cwd); + if (agent.bossClient === "boss_participant" && canonicalCwd === parsePath(canonicalCwd).root) { + throw new Error("boss_participant workspace root must not be a filesystem root"); + } + if (isRecord(agent.sandboxPolicy)) { + assertBossCanonicalData(agent.sandboxPolicy, "$.agent.sandboxPolicy"); + const allowedKeys = type === "workspaceWrite" || type === "workspace-write" ? /* @__PURE__ */ new Set(["type", "writableRoots", "networkAccess"]) : /* @__PURE__ */ new Set(["type", "networkAccess"]); + const keys = Reflect.ownKeys(agent.sandboxPolicy); + if (keys.some((key) => typeof key !== "string" || !allowedKeys.has(key))) { + throw new Error(`${agent.bossClient} sandboxPolicy contains unsupported capability fields`); + } + if (agent.sandboxPolicy.networkAccess !== false) { + throw new Error(`${agent.bossClient} networkAccess must be false`); + } + if (type !== "readOnly" && type !== "read-only" && type !== "workspaceWrite" && type !== "workspace-write") { + throw new Error(`${agent.bossClient} sandboxPolicy type is unsupported`); + } + } + if (isRecord(agent.sandboxPolicy) && (type === "workspaceWrite" || type === "workspace-write")) { + const roots = agent.sandboxPolicy.writableRoots; + assertBossCanonicalData(roots, "$.agent.sandboxPolicy.writableRoots"); + if (!Array.isArray(roots) || nodeUtilTypes2.isProxy(roots) || roots.some((root) => typeof root !== "string")) { + throw new Error(`${agent.bossClient} writableRoots must be a dense string array`); + } + if (agent.bossClient === "boss_reviewer" || roots.length !== 1 || resolve2(roots[0]) !== canonicalCwd) { + throw new Error(`${agent.bossClient} writable roots must be restricted to the agent cwd`); + } + } + if (agent.bossClient === "boss_participant") { + throw new Error("boss_participant requires unavailable broker-owned assigned workspace authority"); + } +} +function bridgeAgentApprovalPolicy(agent) { + return agent.approvalPolicy ?? (agent.bossClient === void 0 ? "never" : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].approvalPolicy); +} +function bridgeAgentDefaultSandbox(agent) { + return agent.bossClient === void 0 ? void 0 : HARDENED_BOSS_CODEX_DEFAULTS[agent.bossClient].sandbox; +} +function assertHardenedBossBridgeConfig(config) { + assertBossCanonicalData(config, "$.bridgeConfig"); + if (nodeUtilTypes2.isProxy(config) || nodeUtilTypes2.isProxy(config.agents) || !Array.isArray(config.agents)) { + throw new Error("Bridge config and agents must be plain non-proxy data"); + } + for (let index = 0; index < config.agents.length; index += 1) { + if (!Object.hasOwn(config.agents, index)) throw new Error("Bridge agents must not be sparse"); + const agent = config.agents[index]; + if (typeof agent !== "object" || agent === null || Array.isArray(agent) || nodeUtilTypes2.isProxy(agent)) { + throw new Error("Bridge agents must be plain non-proxy objects"); + } + } + if (!config.agents.some((agent) => agent.bossClient !== void 0)) return; + if (config.appServer !== void 0) { + assertBossCanonicalData(config.appServer, "$.appServer"); + if (nodeUtilTypes2.isProxy(config.appServer)) throw new Error("Hardened Boss app-server config must not be a proxy"); + if (config.appServer.command !== void 0 || config.appServer.startDaemonCommand !== void 0) { + throw new Error("Hardened Boss bridge cannot use caller-provided app-server commands"); + } + } + for (const args of [config.appServer?.args, config.appServer?.startDaemonArgs]) { + if (!args) continue; + assertBossCanonicalData(args, "$.appServer.argv"); + if (nodeUtilTypes2.isProxy(args) || args.some((arg) => typeof arg !== "string")) throw new Error("Hardened Boss bridge arguments must be dense string arrays"); + for (const arg of args) { + if (arg === "--") { + continue; + } + if (arg.length > 2 && arg.startsWith("-C")) { + throw new Error("Hardened Boss bridge cannot pass launch escape -C to app-server"); + } + const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; + if (["-c", "--config", "-p", "--profile", "--enable", "--disable"].includes(optionName) || optionName.startsWith("-c") && optionName !== "-C" || optionName.startsWith("-p")) { + throw new Error(`Hardened Boss bridge cannot pass raw ${optionName} or profile configuration to app-server`); + } + if (["--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-hook-trust", "--yolo", "--add-dir", "--cd", "-C"].includes(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + if (["--sandbox", "--ask-for-approval"].includes(optionName) || optionName === "-s" || optionName === "-a" || optionName.startsWith("-s") || optionName.startsWith("-a")) { + throw new Error(`Hardened Boss bridge cannot pass policy override ${optionName} to app-server`); + } + if (optionName.startsWith("-") && /(?:yolo|danger|bypass)/i.test(optionName)) { + throw new Error(`Hardened Boss bridge cannot pass launch escape ${optionName} to app-server`); + } + } + } + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); +} function normalizeAgent(raw, index) { if (!isRecord(raw)) throw new Error(`agents[${index}] must be an object`); const id = requireString(raw.id, `agents[${index}].id`); const name = optionalString(raw.name, `agents[${index}].name`) ?? id; - return { + const agent = { id, name, cwd: resolve2(optionalString(raw.cwd, `agents[${index}].cwd`) ?? processCwd()), @@ -584,20 +1028,27 @@ function normalizeAgent(raw, index) { threadId: optionalString(raw.threadId, `agents[${index}].threadId`), instructions: optionalString(raw.instructions, `agents[${index}].instructions`), approvalPolicy: raw.approvalPolicy, - sandboxPolicy: raw.sandboxPolicy + sandboxPolicy: raw.sandboxPolicy, + bossClient: parseHardenedBossClientKind(raw.bossClient, `agents[${index}].bossClient`) }; + assertHardenedBossAgentConfig(agent); + return agent; } function defaultBridgeConfig(env = process.env) { const id = env.CODEX_INTERCOM_BRIDGE_ID?.trim() || "codex-worker"; + const bossClient = parseHardenedBossClientKind(env.CODEX_INTERCOM_BOSS_CLIENT?.trim(), "CODEX_INTERCOM_BOSS_CLIENT"); + const agent = { + id, + name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, + cwd: resolve2(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), + model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || void 0, + instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || void 0, + ...bossClient === void 0 ? {} : { bossClient } + }; + assertHardenedBossAgentConfig(agent); return { statePath: env.CODEX_INTERCOM_BRIDGE_STATE?.trim() || DEFAULT_BRIDGE_STATE_PATH, - agents: [{ - id, - name: env.CODEX_INTERCOM_BRIDGE_NAME?.trim() || id, - cwd: resolve2(env.CODEX_INTERCOM_BRIDGE_CWD?.trim() || processCwd()), - model: env.CODEX_INTERCOM_BRIDGE_MODEL?.trim() || void 0, - instructions: env.CODEX_INTERCOM_BRIDGE_INSTRUCTIONS?.trim() || void 0 - }] + agents: [agent] }; } function loadBridgeConfig(path = process.env.CODEX_INTERCOM_BRIDGE_CONFIG || DEFAULT_BRIDGE_CONFIG_PATH) { @@ -645,166 +1096,11 @@ function saveBridgeState(path, state) { import { EventEmitter as EventEmitter2 } from "events"; import net2 from "net"; import { randomUUID as randomUUID2 } from "crypto"; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy.js -var POLICY_SEMANTICS_VERSION = 2; - -// ../../src/github.com/dataforxyz/agent-intercom-codex/node_modules/@dataforxyz/agent-intercom-core/dist/policy-vectors.js -var localRoot = { - id: "local-root", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-root" -}; -var localPeer = { - id: "local-peer", - kind: "local", - state: "active", - generation: 1, - policy: "local-public", - rootSessionId: "local-peer" -}; -var remoteManager = { - id: "remote-manager", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "local-root", - rootSessionId: "local-root" -}; -var remoteChild = { - id: "remote-child", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var remoteSibling = { - id: "remote-sibling", - kind: "remote", - state: "active", - generation: 1, - policy: "remote-tree", - parentSessionId: "remote-manager", - rootSessionId: "local-root" -}; -var POLICY_VECTORS = [ - { - name: "local sessions remain public", - principals: [localRoot, localPeer], - actorId: "local-root", - action: "send", - targetId: "local-peer", - expectedAllowed: true, - expectedReasonOrCode: "local-public" - }, - { - name: "remote manager can reach direct local parent", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "local parent can reach direct remote child", - principals: [localRoot, remoteManager], - actorId: "local-root", - action: "ask", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "direct-parent" - }, - { - name: "remote child can reach its local root through the ancestor chain", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "send", - targetId: "local-root", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-chain" - }, - { - name: "remote siblings cannot communicate in phase one", - principals: [localRoot, remoteManager, remoteChild, remoteSibling], - actorId: "remote-child", - action: "discover", - targetId: "remote-sibling", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "unrelated local session cannot discover remote principal", - principals: [localRoot, localPeer, remoteManager], - actorId: "local-peer", - action: "discover", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal cannot reach unrelated local session", - principals: [localRoot, localPeer, remoteManager], - actorId: "remote-manager", - action: "send", - targetId: "local-peer", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote manager may inspect its descendant subtree", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-manager", - action: "inspect_tree", - targetId: "remote-child", - expectedAllowed: true, - expectedReasonOrCode: "ancestor-control" - }, - { - name: "remote child cannot revoke its ancestor", - principals: [localRoot, remoteManager, remoteChild], - actorId: "remote-child", - action: "revoke", - targetId: "remote-manager", - expectedAllowed: false, - expectedReasonOrCode: "POLICY_DENIED" - }, - { - name: "remote principal may request attenuated delegation under itself", - principals: [localRoot, remoteManager], - actorId: "remote-manager", - action: "delegate_child", - targetId: "remote-manager", - expectedAllowed: true, - expectedReasonOrCode: "self" - }, - { - name: "revoked principal cannot communicate", - principals: [localRoot, { ...remoteManager, state: "revoked" }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - expectedAllowed: false, - expectedReasonOrCode: "REVOKED_PRINCIPAL" - }, - { - name: "stale actor generation cannot send", - principals: [localRoot, { ...remoteManager, generation: 2 }], - actorId: "remote-manager", - action: "send", - targetId: "local-root", - context: { actorGeneration: 1 }, - expectedAllowed: false, - expectedReasonOrCode: "STALE_GENERATION" - } -]; -var POLICY_SEMANTICS_HASH = "f3b00e503631bc91123aedfbcf1df72cc9913e1893c09728b2c598f3dcdfdfe0"; +import { POLICY_SEMANTICS_HASH, POLICY_SEMANTICS_VERSION } from "@dataforxyz/agent-intercom-core"; +import { + BOSS_RUN_FEATURE as BOSS_RUN_FEATURE2, + parseBrokerCapabilityAdvertisement as parseBrokerCapabilityAdvertisement2 +} from "@dataforxyz/agent-intercom-core/boss"; // broker/framing.ts var MAX_FRAME_BYTES = 1024 * 1024; @@ -985,8 +1281,159 @@ var PersistentOutboundOutbox = class { } }; +// boss-control-outbox.ts +import { createHash as createHash3 } from "node:crypto"; +import { chmodSync as chmodSync3, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync3 } from "node:fs"; +import { join as join4 } from "node:path"; +import { canonicalHash } from "@dataforxyz/agent-intercom-core/canonical"; +import { parseBossControlEnvelope as parseBossControlEnvelope2 } from "@dataforxyz/agent-intercom-core/boss"; +var BOSS_CONTROL_OUTBOX_VERSION = 2; +var MAX_BOSS_CONTROL_OUTBOX_ENTRIES = 256; +function scope(envelope) { + return canonicalHash("agent-intercom-codex/boss-control/outbox-scope/v1", { + bossRunId: envelope.bossRunId, + participantId: envelope.participantId, + bindingEpoch: Number(envelope.bindingEpoch), + idempotencyKey: envelope.idempotencyKey + }); +} +function fingerprint2(to, envelope) { + const { messageId: _transportMessageId, ...stableEnvelope } = envelope; + return canonicalHash("agent-intercom-codex/boss-control/outbox-request/v1", { to, envelope: stableEnvelope }); +} +function exactKeys(value, required, optional = []) { + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + const keys = Reflect.ownKeys(value); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseEntry(value) { + assertBossCanonicalData(value, "$.bossControlOutbox.entries[]"); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Boss outbox entry"); + const entry = value; + if (!exactKeys(entry, ["to", "envelope", "scope", "fingerprint", "queuedAt", "state"], ["deliveryId"])) { + throw new Error("Invalid Boss outbox entry fields"); + } + if (typeof entry.to !== "string" || entry.to.length === 0 || typeof entry.scope !== "string" || !/^[a-f0-9]{64}$/.test(entry.scope) || typeof entry.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(entry.fingerprint) || typeof entry.queuedAt !== "number" || !Number.isSafeInteger(entry.queuedAt) || entry.state !== "queued" && entry.state !== "accepted" || entry.state === "queued" && Object.hasOwn(entry, "deliveryId") || entry.state === "accepted" && (!Object.hasOwn(entry, "deliveryId") || typeof entry.deliveryId !== "string" || entry.deliveryId.length === 0)) throw new Error("Invalid Boss outbox entry binding"); + const envelope = parseBossControlEnvelope2(entry.envelope); + if (entry.scope !== scope(envelope) || entry.fingerprint !== fingerprint2(entry.to, envelope)) { + throw new Error("Boss outbox entry canonical binding mismatch"); + } + return { + to: entry.to, + envelope, + scope: entry.scope, + fingerprint: entry.fingerprint, + queuedAt: entry.queuedAt, + state: entry.state, + ...entry.deliveryId === void 0 ? {} : { deliveryId: entry.deliveryId } + }; +} +function fileName2(sessionId) { + return `${createHash3("sha256").update(sessionId).digest("hex")}.json`; +} +var PersistentBossControlOutbox = class { + path; + state; + constructor(sessionId, intercomDir = getIntercomDirPath()) { + ensureIntercomRuntimeDir(intercomDir); + const directory = join4(intercomDir, "boss-control-outbox"); + mkdirSync4(directory, { recursive: true, mode: INTERCOM_DIR_MODE }); + if (process.platform !== "win32") chmodSync3(directory, INTERCOM_DIR_MODE); + this.path = join4(directory, fileName2(sessionId)); + this.state = this.load(); + } + list() { + return structuredClone(this.state.entries); + } + find(idempotencyKey) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + return entry === void 0 ? void 0 : structuredClone(entry); + } + enqueue(to, envelopeValue) { + if (typeof to !== "string" || to.length === 0) throw new Error("Boss target session ID is required"); + assertBossCanonicalData(envelopeValue, "$.envelope"); + const envelope = parseBossControlEnvelope2(envelopeValue); + const candidateScope = scope(envelope); + const candidateFingerprint = fingerprint2(to, envelope); + const existing = this.state.entries.find((entry) => entry.scope === candidateScope); + if (existing) { + if (existing.fingerprint !== candidateFingerprint) { + throw new Error(`Boss idempotency key ${envelope.idempotencyKey} is queued with a different canonical request`); + } + if (existing.envelope.messageId !== envelope.messageId) { + existing.envelope = envelope; + existing.queuedAt = Date.now(); + this.persist(); + } + return "existing"; + } + if (this.state.entries.some((entry) => entry.envelope.messageId === envelope.messageId)) { + throw new Error(`Boss message ID ${envelope.messageId} is queued with a different idempotency scope`); + } + if (this.state.entries.length >= MAX_BOSS_CONTROL_OUTBOX_ENTRIES) throw new Error("Durable Boss control outbox is full"); + this.state.entries.push({ + to, + envelope, + scope: candidateScope, + fingerprint: candidateFingerprint, + queuedAt: Date.now(), + state: "queued" + }); + this.persist(); + return "added"; + } + markAccepted(idempotencyKey, messageId, deliveryId) { + const entry = this.state.entries.find((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (!entry || entry.envelope.messageId !== messageId || !deliveryId) { + throw new Error("Boss acknowledgement does not match the durable outbox binding"); + } + if (entry.state === "accepted") { + if (entry.deliveryId !== deliveryId) throw new Error("Boss acknowledgement changed the durable deliveryId"); + return "already-accepted"; + } + entry.state = "accepted"; + entry.deliveryId = deliveryId; + this.persist(); + return "accepted"; + } + removeCorrelated(idempotencyKey, messageId, deliveryId) { + const index = this.state.entries.findIndex((candidate) => candidate.envelope.idempotencyKey === idempotencyKey); + if (index < 0) throw new Error("Boss terminal result has no durable outbox binding"); + const entry = this.state.entries[index]; + if (entry.envelope.messageId !== messageId) throw new Error("Boss terminal result messageId does not match the durable caller"); + if (deliveryId === void 0) { + if (entry.state !== "queued") throw new Error("Boss post-acceptance failure omitted the durable deliveryId"); + } else if (entry.state !== "accepted" || entry.deliveryId !== deliveryId) { + throw new Error("Boss terminal result arrived before the matching durable acknowledgement"); + } + this.state.entries.splice(index, 1); + this.persist(); + } + load() { + if (!existsSync3(this.path)) return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: [] }; + try { + const parsed = JSON.parse(readFileSync4(this.path, "utf8")); + assertBossCanonicalData(parsed, "$.bossControlOutbox"); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object"); + const state = parsed; + if (!exactKeys(state, ["version", "entries"]) || state.version !== BOSS_CONTROL_OUTBOX_VERSION || !Array.isArray(state.entries)) { + throw new Error("invalid Boss outbox state"); + } + return { version: BOSS_CONTROL_OUTBOX_VERSION, entries: state.entries.map(parseEntry) }; + } catch (error) { + const corruptPath = `${this.path}.corrupt-${Date.now()}`; + renameSync3(this.path, corruptPath); + restrictIntercomRuntimeFile(corruptPath); + throw new Error(`Boss control outbox was corrupt and quarantined at ${corruptPath}`, { cause: error }); + } + } + persist() { + writeDurableJson(this.path, this.state); + } +}; + // broker/access-credential.ts -import { readFileSync as readFileSync4 } from "fs"; +import { readFileSync as readFileSync5 } from "fs"; var ACCESS_CREDENTIAL_ENV = "AGENT_INTERCOM_ACCESS_CREDENTIAL_PATH"; var ACCESS_CREDENTIAL_VERSION = 1; function nonEmptyString(value) { @@ -995,7 +1442,7 @@ function nonEmptyString(value) { function loadRemoteAccessCredential(env = process.env) { const path = env[ACCESS_CREDENTIAL_ENV]?.trim(); if (!path) return void 0; - const parsed = JSON.parse(readFileSync4(path, "utf8")); + const parsed = JSON.parse(readFileSync5(path, "utf8")); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error(`Invalid Agent Intercom access credential at ${path}`); } @@ -1028,7 +1475,49 @@ function writeRemoteSessionCredential(path, sessionId, metadata) { }); } +// broker/boss-control-ledger.ts +import { canonicalJson as canonicalJson2 } from "@dataforxyz/agent-intercom-core/canonical"; +var BOSS_CONTROL_FAILURE_CODES = /* @__PURE__ */ new Set([ + "INVALID_CONTROL", + "IDEMPOTENCY_CONFLICT", + "SESSION_NOT_FOUND", + "POLICY_DENIED", + "RECIPIENT_DISCONNECTED", + "DELIVERY_TIMEOUT" +]); +function exactStringKeys(value, required, optional = []) { + const keys = Reflect.ownKeys(value); + const permitted = /* @__PURE__ */ new Set([...required, ...optional]); + return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => typeof key === "string" && permitted.has(key)); +} +function parseBossControlResult(value) { + assertBossCanonicalData(value, "$.bossControlResult"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control result must be an exact plain object"); + } + const result = value; + const base = typeof result.requestId === "string" && result.requestId.length > 0 && result.messageId === result.requestId && typeof result.idempotencyKey === "string" && result.idempotencyKey.length > 0; + if (!base || result.type !== "boss_control_result") throw new Error("Invalid Boss control result binding"); + if (result.status === "delivered" && result.delivered === true && typeof result.deliveryId === "string" && result.deliveryId.length > 0 && exactStringKeys(result, ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "deliveryId"])) return result; + if (result.status === "rejected" && result.delivered === false && typeof result.code === "string" && BOSS_CONTROL_FAILURE_CODES.has(result.code) && typeof result.reason === "string" && result.reason.length > 0 && (!Object.hasOwn(result, "deliveryId") || typeof result.deliveryId === "string" && result.deliveryId.length > 0) && exactStringKeys( + result, + ["type", "requestId", "messageId", "idempotencyKey", "status", "delivered", "code", "reason"], + ["deliveryId"] + )) return result; + throw new Error("Invalid Boss control result discriminant"); +} +function parseBossControlAck(value) { + assertBossCanonicalData(value, "$.bossControlAck"); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Boss control acknowledgement must be an exact plain object"); + } + const ack = value; + if (!exactStringKeys(ack, ["type", "requestId", "messageId", "idempotencyKey", "status", "deliveryId"]) || ack.type !== "boss_control_ack" || typeof ack.requestId !== "string" || ack.requestId.length === 0 || ack.messageId !== ack.requestId || typeof ack.idempotencyKey !== "string" || ack.idempotencyKey.length === 0 || ack.status !== "accepted" || typeof ack.deliveryId !== "string" || ack.deliveryId.length === 0) throw new Error("Invalid Boss control acknowledgement discriminant"); + return ack; +} + // broker/client.ts +import { types as nodeUtilTypes3 } from "node:util"; function toError(error) { return error instanceof Error ? error : new Error(String(error)); } @@ -1098,6 +1587,13 @@ function isSessionInfo(value) { for (const field of ["depth", "maxDepth", "maxChildren"]) { if (session[field] !== void 0 && (typeof session[field] !== "number" || !Number.isSafeInteger(session[field]))) return false; } + if (session.boss !== void 0) { + try { + parseBossParticipantBindingMetadata(session.boss, session.id); + } catch { + return false; + } + } return true; } function isRemoteAccessMetadata(value) { @@ -1111,8 +1607,12 @@ var IntercomClient = class extends EventEmitter2 { pendingSends = /* @__PURE__ */ new Map(); pendingLists = /* @__PURE__ */ new Map(); pendingAskControls = /* @__PURE__ */ new Map(); + pendingBossControls = /* @__PURE__ */ new Map(); outbox = null; + bossControlOutbox = null; remoteAccessCredential; + requestedBossRegistration; + _bossBinding; disconnecting = false; disconnectError = null; failPending(error) { @@ -1129,6 +1629,11 @@ var IntercomClient = class extends EventEmitter2 { pending.resolve(false); } this.pendingAskControls.clear(); + for (const pending of this.pendingBossControls.values()) { + clearTimeout(pending.timeout); + pending.reject(error); + } + this.pendingBossControls.clear(); } get sessionId() { return this._sessionId; @@ -1136,6 +1641,12 @@ var IntercomClient = class extends EventEmitter2 { get outboxSize() { return this.outbox?.list().length ?? 0; } + get bossBinding() { + return this._bossBinding; + } + get bossControlOutboxSize() { + return this.bossControlOutbox?.list().length ?? 0; + } isConnected() { const socket = this.socket; return Boolean(socket && this._sessionId && !this.disconnecting && !socket.destroyed && !socket.writableEnded && socket.writable); @@ -1157,6 +1668,19 @@ var IntercomClient = class extends EventEmitter2 { if (this.socket) { return Promise.reject(new Error("Already connected")); } + try { + const canonicalSession = parseExactRegistrationFrame({ + type: "register", + ...typeof session === "object" && session !== null && !nodeUtilTypes3.isProxy(session) && Object.getOwnPropertyDescriptor(session, "boss") !== void 0 ? { registrationKind: "boss" } : {}, + protocol: INTERCOM_PROTOCOL_NAME, + version: INTERCOM_PROTOCOL_VERSION, + session + }).session; + this.requestedBossRegistration = session.boss === void 0 ? void 0 : parseBossParticipantRegistrationMetadata(session.boss); + if (canonicalSession !== session) throw new Error("Registration session identity changed during validation"); + } catch (error) { + return Promise.reject(toError(error)); + } return new Promise((resolve5, reject) => { let socket; let target; @@ -1211,6 +1735,8 @@ var IntercomClient = class extends EventEmitter2 { this.socket = null; } this._sessionId = null; + this._bossBinding = void 0; + this.requestedBossRegistration = void 0; this.disconnectError = null; if (connectionEstablished && !wasDisconnecting) { this.emit("disconnected", disconnectError); @@ -1256,6 +1782,7 @@ var IntercomClient = class extends EventEmitter2 { try { writeMessage(socket, { type: "register", + ...session.boss === void 0 ? {} : { registrationKind: "boss" }, protocol: INTERCOM_PROTOCOL_NAME, version: INTERCOM_PROTOCOL_VERSION, session, @@ -1275,15 +1802,21 @@ var IntercomClient = class extends EventEmitter2 { }); } handleBrokerMessage(msg) { - if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") { + if (typeof msg !== "object" || msg === null || nodeUtilTypes3.isProxy(msg)) { throw new Error("Invalid broker message"); } + const typeDescriptor = Object.getOwnPropertyDescriptor(msg, "type"); + if (typeDescriptor === void 0 || !typeDescriptor.enumerable || !Object.hasOwn(typeDescriptor, "value") || typeof typeDescriptor.value !== "string") throw new Error("Invalid broker message"); const brokerMessage = msg; if (this._sessionId === null && brokerMessage.type !== "registered" && brokerMessage.type !== "error") { throw new Error(`Received ${brokerMessage.type} before registered`); } switch (brokerMessage.type) { case "registered": { + parseExactRegisteredFrame( + brokerMessage, + this.requestedBossRegistration === void 0 ? this.remoteAccessCredential === void 0 ? "ordinary-local" : "ordinary-remote" : "boss" + ); if (typeof brokerMessage.sessionId !== "string" || brokerMessage.protocol !== INTERCOM_PROTOCOL_NAME || brokerMessage.version !== INTERCOM_PROTOCOL_VERSION) { throw new Error("Invalid registered message"); } @@ -1308,9 +1841,28 @@ var IntercomClient = class extends EventEmitter2 { } } } + if (this.requestedBossRegistration !== void 0) { + if (brokerMessage.remoteAccess !== void 0 || brokerMessage.access !== void 0) { + throw new Error("Boss registration returned folded remote-access metadata"); + } + const advertisement = parseBrokerCapabilityAdvertisement2(brokerMessage.capabilities); + if (!advertisement.features.some((feature) => feature.feature === BOSS_RUN_FEATURE2)) { + throw new Error("Broker did not echo the required boss-run-v1 feature contract"); + } + const binding = parseBossParticipantBindingMetadata(brokerMessage.boss, brokerMessage.sessionId); + const credential = this.requestedBossRegistration.credential; + if (binding.featureContract.feature !== this.requestedBossRegistration.featureContract.feature || binding.binding.bossRunId !== credential.bossRunId || binding.binding.participantId !== credential.participantId || binding.binding.role !== credential.role || binding.binding.communicationProfile !== credential.communicationProfile || binding.binding.bindingEpoch !== credential.bindingEpoch) { + throw new Error("Broker returned a Boss binding that does not match the authenticated registration request"); + } + this._bossBinding = binding; + } else if (brokerMessage.boss !== void 0) { + throw new Error("Broker attached unsolicited Boss binding metadata to an ordinary registration"); + } this._sessionId = brokerMessage.sessionId; this.outbox = new PersistentOutboundOutbox(brokerMessage.sessionId); + this.bossControlOutbox = this._bossBinding === void 0 ? null : new PersistentBossControlOutbox(brokerMessage.sessionId); this.replayOutbox(); + this.replayBossControlOutbox(); this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId }); break; } @@ -1335,6 +1887,48 @@ var IntercomClient = class extends EventEmitter2 { this.emit("message", from, message, deliveryId); break; } + case "boss_control": { + const { deliveryId, from } = brokerMessage; + if (typeof deliveryId !== "string" || !isSessionInfo(from)) { + throw new Error("Invalid boss_control event"); + } + const envelope = bossControlKind(brokerMessage.envelope).envelope; + const source = from.boss === void 0 ? void 0 : parseBossParticipantBindingMetadata(from.boss, from.id).binding; + if (source === void 0 || source.state !== "active" || source.bossRunId !== envelope.bossRunId || source.participantId !== envelope.participantId || source.bindingEpoch !== envelope.bindingEpoch) throw new Error("Boss control event sender does not match its broker-owned binding"); + this.emit("boss_control", from, envelope, deliveryId); + break; + } + case "boss_control_result": { + const result = parseBossControlResult(brokerMessage); + const { requestId, messageId, idempotencyKey, deliveryId } = result; + const stored = this.bossControlOutbox?.find(idempotencyKey); + if (!stored || stored.envelope.messageId !== requestId) throw new Error("Boss control result does not match the durable outbox binding"); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control result correlation does not match the pending request"); + } + this.bossControlOutbox.removeCorrelated(idempotencyKey, messageId, deliveryId); + if (pending) { + clearTimeout(pending.timeout); + this.pendingBossControls.delete(requestId); + pending.resolve(result); + } + break; + } + case "boss_control_ack": { + const { requestId, messageId, idempotencyKey, deliveryId } = parseBossControlAck(brokerMessage); + const pending = this.pendingBossControls.get(requestId); + if (pending && (pending.messageId !== messageId || pending.idempotencyKey !== idempotencyKey)) { + throw new Error("Boss control acknowledgement correlation does not match the pending request"); + } + const transition = this.bossControlOutbox?.markAccepted(idempotencyKey, messageId, deliveryId); + if (transition === void 0) throw new Error("Boss control acknowledgement has no durable outbox"); + if (pending?.deliveryId !== void 0 && pending.deliveryId !== deliveryId) { + throw new Error("Boss control acknowledgement changed the pending deliveryId"); + } + if (pending) pending.deliveryId = deliveryId; + break; + } case "delivery_accepted": { const { deliveryId, messageId } = brokerMessage; if (typeof deliveryId !== "string" || typeof messageId !== "string") { @@ -1585,6 +2179,64 @@ var IntercomClient = class extends EventEmitter2 { } }); } + sendBossControl(to, envelopeValue) { + let socket; + try { + socket = this.requireActiveSocket(); + } catch (error) { + return Promise.reject(toError(error)); + } + let envelope; + try { + envelope = bossControlKind(envelopeValue).envelope; + const binding = this._bossBinding?.binding; + if (binding === void 0 || binding.state !== "active" || envelope.bossRunId !== binding.bossRunId || envelope.participantId !== binding.participantId || envelope.bindingEpoch !== binding.bindingEpoch) throw new Error("Boss control envelope does not match this client's active participant binding"); + } catch (error) { + return Promise.reject(toError(error)); + } + const requestId = envelope.messageId; + if (this.pendingBossControls.has(requestId)) { + return Promise.resolve({ + requestId, + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + status: "rejected", + delivered: false, + code: "INVALID_CONTROL", + reason: "Boss requestId is already pending" + }); + } + try { + if (!this.bossControlOutbox) throw new Error("Durable Boss control outbox is unavailable"); + this.bossControlOutbox.enqueue(to, envelope); + } catch (error) { + return Promise.reject(toError(error)); + } + return new Promise((resolve5, reject) => { + const timeout = setTimeout(() => { + if (!this.pendingBossControls.delete(requestId)) return; + reject(new Error("Boss control delivery timeout")); + }, 1e4); + timeout.unref?.(); + this.pendingBossControls.set(requestId, { + messageId: envelope.messageId, + idempotencyKey: envelope.idempotencyKey, + resolve: resolve5, + reject, + timeout + }); + try { + writeMessage(socket, { type: "boss_control", requestId, to, envelope }); + } catch (error) { + clearTimeout(timeout); + this.pendingBossControls.delete(requestId); + reject(toError(error)); + } + }); + } + acknowledgeBossControl(deliveryId, messageId, idempotencyKey) { + return this.writeControlMessage({ type: "boss_control_received", deliveryId, messageId, idempotencyKey }); + } acknowledgeMessage(deliveryId) { return this.writeControlMessage({ type: "message_received", deliveryId }); } @@ -1640,6 +2292,22 @@ var IntercomClient = class extends EventEmitter2 { } } } + replayBossControlOutbox() { + const socket = this.socket; + if (!socket || socket.destroyed || !this._sessionId || !this.bossControlOutbox) return; + for (const entry of this.bossControlOutbox.list()) { + try { + writeMessage(socket, { + type: "boss_control", + requestId: entry.envelope.messageId, + to: entry.to, + envelope: entry.envelope + }); + } catch { + return; + } + } + } updatePresence(updates) { if (this.disconnecting) { return; @@ -1654,38 +2322,39 @@ var IntercomClient = class extends EventEmitter2 { // broker/spawn.ts import { spawn as spawn2 } from "child_process"; -import { existsSync as existsSync3, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3 } from "fs"; -import { join as join4, dirname as dirname3 } from "path"; +import { existsSync as existsSync4, readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs"; +import { join as join5, dirname as dirname3 } from "path"; import { fileURLToPath } from "url"; import { createRequire } from "module"; import net3 from "net"; import { randomUUID as randomUUID3 } from "crypto"; +import { POLICY_SEMANTICS_HASH as POLICY_SEMANTICS_HASH2, POLICY_SEMANTICS_VERSION as POLICY_SEMANTICS_VERSION2 } from "@dataforxyz/agent-intercom-core"; var INTERCOM_DIR = getIntercomDirPath(); -var EXTENSION_DIR = join4(dirname3(fileURLToPath(import.meta.url)), ".."); -var BROKER_PID = join4(INTERCOM_DIR, "broker.pid"); -var BROKER_SPAWN_LOCK = join4(INTERCOM_DIR, "broker.spawn.lock"); +var EXTENSION_DIR = join5(dirname3(fileURLToPath(import.meta.url)), ".."); +var BROKER_PID = join5(INTERCOM_DIR, "broker.pid"); +var BROKER_SPAWN_LOCK = join5(INTERCOM_DIR, "broker.spawn.lock"); function sleep(ms) { return new Promise((resolve5) => setTimeout(resolve5, ms)); } function getBrokerEntryPath(moduleUrl = import.meta.url) { const moduleDir = dirname3(fileURLToPath(moduleUrl)); - const bundledBroker = join4(moduleDir, "broker.mjs"); - return existsSync3(bundledBroker) ? bundledBroker : join4(moduleDir, "broker.ts"); + const bundledBroker = join5(moduleDir, "broker.mjs"); + return existsSync4(bundledBroker) ? bundledBroker : join5(moduleDir, "broker.ts"); } function getTsxCliPath(extensionDir = EXTENSION_DIR) { try { const requireFromExtension = createRequire(import.meta.url); const tsxMain = requireFromExtension.resolve("tsx"); - return join4(dirname3(tsxMain), "cli.mjs"); + return join5(dirname3(tsxMain), "cli.mjs"); } catch { - return join4(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); + return join5(extensionDir, "node_modules", "tsx", "dist", "cli.mjs"); } } function quoteWindowsArg(value) { return `"${value.replace(/"/g, '""')}"`; } function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) { - return join4(intercomDir, "broker-launch.vbs"); + return join5(intercomDir, "broker-launch.vbs"); } function usesDefaultBrokerCommand(brokerCommand, brokerArgs) { return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx"; @@ -1713,7 +2382,7 @@ function isBrokerHealthOkMessage(message, requestId) { const remoteAccess = response.remoteAccess; if (typeof remoteAccess !== "object" || remoteAccess === null || Array.isArray(remoteAccess)) return false; const contract = remoteAccess; - return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION && contract.policySemanticsHash === POLICY_SEMANTICS_HASH; + return contract.feature === "remote-access-v1" && contract.policySemanticsVersion === POLICY_SEMANTICS_VERSION2 && contract.policySemanticsHash === POLICY_SEMANTICS_HASH2; } function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) { ensureIntercomRuntimeDir(dirname3(launcherPath)); @@ -1819,10 +2488,10 @@ async function spawnBrokerIfNeeded(brokerCommand, brokerArgs) { } } async function stopBrokerProcess(pidFile = BROKER_PID, timeoutMs = 3e3) { - if (!existsSync3(pidFile)) return; + if (!existsSync4(pidFile)) return; let pid; try { - pid = Number.parseInt(readFileSync5(pidFile, "utf-8").trim(), 10); + pid = Number.parseInt(readFileSync6(pidFile, "utf-8").trim(), 10); } catch { return; } @@ -1847,9 +2516,9 @@ async function isBrokerRunning() { if (await checkSocketConnectable()) { return true; } - if (!existsSync3(BROKER_PID)) return false; + if (!existsSync4(BROKER_PID)) return false; try { - const pid = parseInt(readFileSync5(BROKER_PID, "utf-8").trim(), 10); + const pid = parseInt(readFileSync6(BROKER_PID, "utf-8").trim(), 10); if (!Number.isFinite(pid)) return false; process.kill(pid, 0); return checkSocketConnectable(); @@ -1946,11 +2615,11 @@ ${Date.now()} return false; } function isSpawnLockStale() { - if (!existsSync3(BROKER_SPAWN_LOCK)) { + if (!existsSync4(BROKER_SPAWN_LOCK)) { return false; } try { - const [pidLine = "", createdAtLine = "0"] = readFileSync5(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); + const [pidLine = "", createdAtLine = "0"] = readFileSync6(BROKER_SPAWN_LOCK, "utf-8").trim().split("\n"); const pid = Number.parseInt(pidLine, 10); const createdAt = Number.parseInt(createdAtLine, 10); const ageMs = Date.now() - createdAt; @@ -1984,8 +2653,8 @@ async function waitForBroker(timeoutMs = 5e3) { } // config.ts -import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs"; -import { join as join5, resolve as resolve3 } from "path"; +import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs"; +import { join as join6, resolve as resolve3 } from "path"; import { homedir as homedir2 } from "os"; var DEFAULT_ASK_TIMEOUT_MS = 45 * 1e3; var MAX_ASK_TIMEOUT_MS = 120 * 1e3; @@ -1999,8 +2668,8 @@ function validateAskTimeoutMs(value, name = "timeout_ms") { return value; } function getConfigPath() { - const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve3(process.env.PI_CODING_AGENT_DIR) : join5(homedir2(), ".pi", "agent"); - return join5(agentDir, "intercom", "config.json"); + const agentDir = process.env.PI_CODING_AGENT_DIR ? resolve3(process.env.PI_CODING_AGENT_DIR) : join6(homedir2(), ".pi", "agent"); + return join6(agentDir, "intercom", "config.json"); } var defaults = { brokerCommand: "npx", @@ -2017,11 +2686,11 @@ var defaults = { }; function loadConfig() { const configPath = getConfigPath(); - if (!existsSync4(configPath)) { + if (!existsSync5(configPath)) { return { ...defaults }; } try { - const raw = readFileSync6(configPath, "utf-8"); + const raw = readFileSync7(configPath, "utf-8"); const parsed = JSON.parse(raw); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { throw new Error("Config must be a JSON object"); @@ -2148,39 +2817,126 @@ async function resolveContactTarget(id, name, listSessions) { // codex/team.ts import { readFile } from "node:fs/promises"; -import { join as join6 } from "node:path"; -var LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +import { join as join7 } from "node:path"; +import { + BOSS_PARTICIPANT_ROLES, + parseParticipantState as parseParticipantState2, + parseWorkerIdentityV2 as parseWorkerIdentityV22, + workerIdentityFromEnvironment +} from "@dataforxyz/agent-intercom-core/boss"; +var LEGACY_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "running", "idle", "needs_attention", "stopping"]); +var CANONICAL_LIVE_STATES = /* @__PURE__ */ new Set(["provisioning", "registering", "ready", "working", "waiting", "paused", "stalled", "blocked", "unreachable"]); var stringValue = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0; var connectedTo = (sessions, target) => { const normalized = target.toLowerCase(); return sessions.some((session) => session.id === target || session.name?.toLowerCase() === normalized); }; +function bossIdentityFromEnvironment(env) { + const bossKeys = ["AGENT_INTERCOM_BOSS_RUN_ID", "AGENT_INTERCOM_PARTICIPANT_ID", "AGENT_INTERCOM_BINDING_EPOCH"]; + if (!bossKeys.some((key) => env[key] !== void 0)) return void 0; + const identity = workerIdentityFromEnvironment(env); + if (!("bossRunId" in identity)) throw new Error("Incomplete Boss worker identity cannot discover a team"); + return identity; +} +function canonicalWorker(value) { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("worker must be an object"); + const worker = value; + const identity = parseWorkerIdentityV22({ + version: "orc.worker-identity.v2", + workerId: worker.id, + workerIncarnationId: worker.workerIncarnationId, + workerGeneration: worker.workerGeneration, + ...worker.bossRunId === void 0 ? {} : { bossRunId: worker.bossRunId }, + ...worker.participantId === void 0 ? {} : { participantId: worker.participantId }, + ...worker.bindingEpoch === void 0 ? {} : { bindingEpoch: worker.bindingEpoch } + }); + parseParticipantState2(worker.state, "$.worker.state"); + if (typeof worker.role !== "string" || !BOSS_PARTICIPANT_ROLES.includes(worker.role)) { + throw new Error("worker role is not canonical"); + } + if (worker.owned !== true || !stringValue(worker.managerSessionId) || !stringValue(worker.intercomTarget)) { + throw new Error("canonical worker ownership routing is incomplete"); + } + return { ...worker, canonicalIdentity: identity }; +} +function exactBossRosterSession(sessions, worker) { + const identity = worker.canonicalIdentity; + const target = stringValue(worker.intercomTarget); + const role = stringValue(worker.role); + const state = stringValue(worker.state); + if (!identity || !("bossRunId" in identity) || !target || !role || !state) return void 0; + const matches = sessions.filter((candidate) => candidate.id === target); + if (matches.length !== 1) return void 0; + const [session] = matches; + if (!session?.boss?.binding || session.boss.workerIdentity === void 0 || session.boss.participantState === void 0) return void 0; + try { + const sessionIdentity = parseWorkerIdentityV22(session.boss.workerIdentity); + const sessionState = parseParticipantState2(session.boss.participantState, "$.session.boss.participantState"); + const binding = session.boss.binding; + return "bossRunId" in sessionIdentity && session.id === target && binding.sessionId === session.id && binding.state === "active" && binding.bossRunId === identity.bossRunId && binding.participantId === identity.participantId && binding.bindingEpoch === identity.bindingEpoch && binding.role === role && sessionIdentity.workerId === identity.workerId && sessionIdentity.workerIncarnationId === identity.workerIncarnationId && sessionIdentity.workerGeneration === identity.workerGeneration && sessionIdentity.bossRunId === identity.bossRunId && sessionIdentity.participantId === identity.participantId && sessionIdentity.bindingEpoch === identity.bindingEpoch && sessionState === state ? session : void 0; + } catch { + return void 0; + } +} async function readWorkers(agentDir) { try { - const parsed = JSON.parse(await readFile(join6(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); - return Array.isArray(parsed.workers) ? parsed.workers : []; + const parsed = JSON.parse(await readFile(join7(agentDir, "intercom", "orchestrator", "workers.json"), "utf8")); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("worker snapshot must be an object"); + const snapshot = parsed; + if (snapshot.version !== 1 && snapshot.version !== 2 || !Array.isArray(snapshot.workers)) throw new Error("unsupported worker snapshot version"); + if (snapshot.version === 1) return { version: 1, workers: snapshot.workers }; + return { version: 2, workers: snapshot.workers.map(canonicalWorker) }; } catch { - return []; + return { version: 1, workers: [] }; } } async function resolveIntercomTeam(input) { const env = input.env ?? process.env; - const workers = await readWorkers(input.agentDir ?? getAgentDirPath()); + const snapshot = await readWorkers(input.agentDir ?? getAgentDirPath()); + const workers = snapshot.workers; const workerId = stringValue(env.AGENT_INTERCOM_WORKER_ID); + const bossIdentity = bossIdentityFromEnvironment(env); const runId = stringValue(env.AGENT_INTERCOM_RUN_ID); - const current = workerId ? workers.find((worker) => stringValue(worker.id) === workerId && (!runId || stringValue(worker.runId) === runId)) : void 0; - const managerTarget = stringValue(current?.managerSessionId) ?? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID); + const currentMatches = workerId ? workers.filter((worker) => stringValue(worker.id) === workerId && (bossIdentity === void 0 ? !runId || stringValue(worker.runId) === runId : snapshot.version === 2 && worker.canonicalIdentity?.workerId === bossIdentity.workerId && worker.canonicalIdentity.workerIncarnationId === bossIdentity.workerIncarnationId && worker.canonicalIdentity.workerGeneration === bossIdentity.workerGeneration && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId && worker.canonicalIdentity.participantId === bossIdentity.participantId && worker.canonicalIdentity.bindingEpoch === bossIdentity.bindingEpoch)) : []; + const current = bossIdentity === void 0 ? currentMatches[0] : currentMatches.length === 1 ? currentMatches[0] : void 0; + const currentTarget = stringValue(current?.intercomTarget); + const exactCurrentProjection = current !== void 0 && currentTarget === input.selfId && workers.filter((worker) => stringValue(worker.id) === workerId).length === 1 && workers.filter((worker) => stringValue(worker.intercomTarget) === currentTarget).length === 1 && exactBossRosterSession(input.sessions, current) !== void 0; + if (bossIdentity !== void 0 && !exactCurrentProjection) { + return { self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: false }, coworkers: [] }; + } + const managerTarget = stringValue(current?.managerSessionId) ?? (bossIdentity === void 0 ? stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ?? stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID) : void 0); const teamId = managerTarget ?? input.selfId; - const coworkers = workers.filter((worker) => worker.owned === true).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { + const currentRole = stringValue(current?.role); + const canDiscoverOwnedRoster = bossIdentity === void 0 || currentRole === "manager" || currentRole === "controller"; + const coworkers = (canDiscoverOwnedRoster ? workers : []).filter((worker) => worker.owned === true).filter((worker) => bossIdentity === void 0 || snapshot.version === 2 && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId).filter((worker) => stringValue(worker.managerSessionId) === teamId).filter((worker) => stringValue(worker.intercomTarget) !== managerTarget).filter((worker) => (snapshot.version === 2 ? CANONICAL_LIVE_STATES : LEGACY_LIVE_STATES).has(stringValue(worker.state) ?? "")).filter((worker) => stringValue(worker.id) !== workerId).map((worker) => { const id = stringValue(worker.id); if (!id) return void 0; const target = stringValue(worker.intercomTarget) ?? id; - return { id, target, ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, connected: connectedTo(input.sessions, target) }; + const connected = bossIdentity === void 0 ? connectedTo(input.sessions, target) : exactBossRosterSession(input.sessions, worker) !== void 0; + if (!connected) return void 0; + return { + id, + target, + ...stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}, + ...stringValue(worker.role) ? { role: stringValue(worker.role) } : {}, + ...stringValue(worker.state) ? { state: stringValue(worker.state) } : {}, + connected + }; }).filter((member) => Boolean(member)); - return { teamId, self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: !managerTarget }, manager: managerTarget ? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } : { target: input.selfId, connected: true }, coworkers }; + const managerWorker = managerTarget === void 0 ? void 0 : workers.find((worker) => stringValue(worker.intercomTarget) === managerTarget && (bossIdentity === void 0 || snapshot.version === 2 && stringValue(worker.role) === "manager" && worker.canonicalIdentity !== void 0 && "bossRunId" in worker.canonicalIdentity && "bossRunId" in bossIdentity && worker.canonicalIdentity.bossRunId === bossIdentity.bossRunId)); + const managerConnected = managerTarget === void 0 ? true : bossIdentity === void 0 ? connectedTo(input.sessions, managerTarget) : managerWorker !== void 0 && exactBossRosterSession(input.sessions, managerWorker) !== void 0; + return { + teamId, + self: { id: input.selfId, ...workerId ? { workerId } : {}, isManager: bossIdentity === void 0 && !managerTarget }, + ...managerTarget ? { manager: { target: managerTarget, connected: managerConnected } } : bossIdentity === void 0 ? { manager: { target: input.selfId, connected: true } } : {}, + coworkers + }; } function formatIntercomTeam(team) { - const lines = [`Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}`]; + const lines = [ + `Manager: ${team.manager ? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]` : "unknown"}`, + `You: ${team.self.workerId ?? team.self.id}${team.self.isManager ? " [manager]" : ""}` + ]; if (!team.coworkers.length) lines.push("Coworkers: none"); else { lines.push("Coworkers:"); @@ -2324,6 +3080,25 @@ function threadSandboxMode(sandboxPolicy) { return "read-only"; } } +function protectedBossClientForBridge(config) { + assertBossCanonicalData(config, "$.bridgeConfig"); + if (!Array.isArray(config.agents)) return void 0; + for (const agent of config.agents) { + if (typeof agent !== "object" || agent === null || Array.isArray(agent)) continue; + if (agent.bossClient === "boss_participant" || agent.bossClient === "boss_reviewer") return agent.bossClient; + } + return void 0; +} +function bridgeAgentSandboxMode(agent) { + return agent.sandboxPolicy === void 0 ? bridgeAgentDefaultSandbox(agent) ?? "read-only" : threadSandboxMode(agent.sandboxPolicy); +} +function bridgeAgentTurnSandboxPolicy(agent) { + if (agent.sandboxPolicy !== void 0) return agent.sandboxPolicy; + if (bridgeAgentSandboxMode(agent) === "workspace-write") { + throw new Error("workspace-write requires unavailable broker-owned assigned workspace authority"); + } + return { type: "readOnly", networkAccess: false }; +} function getTurnId(result) { const turn = result && typeof result === "object" ? result.turn : void 0; if (!turn || typeof turn !== "object" || typeof turn.id !== "string") { @@ -2607,12 +3382,12 @@ var VirtualCodexAgent = class { async ensureThread() { if (this.threadId) { try { - const sandbox2 = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox2 = bridgeAgentSandboxMode(this.agent); await this.app.request("thread/resume", { threadId: this.threadId, cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox: sandbox2 }); return this.threadId; @@ -2620,11 +3395,11 @@ var VirtualCodexAgent = class { this.threadId = null; } } - const sandbox = threadSandboxMode(this.agent.sandboxPolicy); + const sandbox = bridgeAgentSandboxMode(this.agent); const result = await this.app.request("thread/start", { cwd: this.agent.cwd, model: this.agent.model ?? null, - approvalPolicy: this.agent.approvalPolicy ?? "never", + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), sandbox, serviceName: "codex-intercom", developerInstructions: this.agent.instructions ?? null, @@ -2675,8 +3450,8 @@ var VirtualCodexAgent = class { threadId, input, cwd: this.agent.cwd, - approvalPolicy: this.agent.approvalPolicy ?? "never", - sandboxPolicy: this.agent.sandboxPolicy ?? { type: "readOnly", networkAccess: false }, + approvalPolicy: bridgeAgentApprovalPolicy(this.agent), + sandboxPolicy: bridgeAgentTurnSandboxPolicy(this.agent), model: this.agent.model ?? null }); } @@ -2891,7 +3666,11 @@ var CodexBridgeDaemon = class { constructor(config, hooks = {}) { this.config = config; this.hooks = hooks; - this.app = new CodexAppServerClient(config.appServer); + const protectedBossClient = protectedBossClientForBridge(config); + assertHardenedBossProviderAuthority(protectedBossClient); + assertHardenedBossBridgeConfig(config); + for (const agent of config.agents) assertHardenedBossAgentConfig(agent); + this.app = new CodexAppServerClient(config.appServer, protectedBossClient); this.app.setServerRequestHandler((message) => this.handleServerRequest(message)); } config; @@ -2900,6 +3679,9 @@ var CodexBridgeDaemon = class { agents = []; inflightToolCalls = /* @__PURE__ */ new Map(); async start() { + assertHardenedBossProviderAuthority(protectedBossClientForBridge(this.config)); + assertHardenedBossBridgeConfig(this.config); + for (const agent of this.config.agents) assertHardenedBossAgentConfig(agent); const intercomConfig = loadConfig(); await spawnBrokerIfNeeded(intercomConfig.brokerCommand, intercomConfig.brokerArgs); await this.app.connect(); @@ -3259,7 +4041,12 @@ var CODEX_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([ var COI_STATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3; var MANAGED_MCP_ENV_KEYS = [ "AGENT_INTERCOM_WORKER_ID", + "AGENT_INTERCOM_WORKER_INCARNATION_ID", + "AGENT_INTERCOM_WORKER_GENERATION", "AGENT_INTERCOM_RUN_ID", + "AGENT_INTERCOM_BOSS_RUN_ID", + "AGENT_INTERCOM_PARTICIPANT_ID", + "AGENT_INTERCOM_BINDING_EPOCH", "AGENT_INTERCOM_MANAGER_TARGET", "AGENT_INTERCOM_MANAGER_SESSION_ID", "AGENT_INTERCOM_SYSTEMD_UNIT", @@ -3270,7 +4057,7 @@ function sanitizeSegment(value) { return value.replace(/[^a-zA-Z0-9._:-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "codex"; } function shortHash(value) { - return createHash3("sha1").update(value).digest("hex").slice(0, 8); + return createHash4("sha1").update(value).digest("hex").slice(0, 8); } function gitString(cwd, args) { const result = spawnSync2("git", args, { @@ -3337,26 +4124,27 @@ function deriveBridgeAgentRuntimeConfig(args, cwd) { let approvalPolicy; let sandboxMode; const writableRoots = /* @__PURE__ */ new Set([resolve4(cwd)]); - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; + const { optionArgs } = splitCodexResumeArgs(args); + for (let index = 0; index < optionArgs.length; index += 1) { + const arg = optionArgs[index]; const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; switch (optionName) { case "--ask-for-approval": case "-a": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); approvalPolicy = parsed.value; index = parsed.nextIndex; break; } case "--sandbox": case "-s": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); sandboxMode = parsed.value; index = parsed.nextIndex; break; } case "--add-dir": { - const parsed = readCodexFlagValue(args, index, optionName); + const parsed = readCodexFlagValue(optionArgs, index, optionName); writableRoots.add(resolve4(cwd, parsed.value)); index = parsed.nextIndex; break; @@ -3370,13 +4158,79 @@ function deriveBridgeAgentRuntimeConfig(args, cwd) { break; } } - const sandboxType = sandboxMode ? camelSandboxType(sandboxMode) : void 0; - const sandboxPolicy = sandboxType === "workspaceWrite" ? { type: sandboxType, writableRoots: [...writableRoots], networkAccess: false } : sandboxType ? { type: sandboxType, ...sandboxType === "readOnly" ? { networkAccess: false } : {} } : void 0; + const sandboxType2 = sandboxMode ? camelSandboxType(sandboxMode) : void 0; + const sandboxPolicy = sandboxType2 === "workspaceWrite" ? { type: sandboxType2, writableRoots: [...writableRoots], networkAccess: false } : sandboxType2 ? { type: sandboxType2, ...sandboxType2 === "readOnly" ? { networkAccess: false } : {} } : void 0; return { ...approvalPolicy ? { approvalPolicy } : {}, ...sandboxPolicy ? { sandboxPolicy } : {} }; } +var HARDENED_BOSS_RAW_CONFIG_OPTIONS = /* @__PURE__ */ new Set(["-c", "--config", "-p", "--profile", "--enable", "--disable"]); +var HARDENED_BOSS_BYPASS_OPTIONS = /* @__PURE__ */ new Set([ + "--dangerously-bypass-approvals-and-sandbox", + "--dangerously-bypass-hook-trust", + "--yolo" +]); +function isHardenedBossRawConfigOption(optionName) { + return HARDENED_BOSS_RAW_CONFIG_OPTIONS.has(optionName) || optionName.startsWith("-c") && optionName !== "-C" || optionName.startsWith("-p"); +} +function hardenedBossAttachedAuthorityOption(arg) { + if (arg.length <= 2) return void 0; + const option = arg.slice(0, 2); + return option === "-s" || option === "-a" || option === "-C" ? option : void 0; +} +function assertHardenedBossCoiLaunch(args, cwd, bossClient) { + if (bossClient !== void 0) { + assertBossCanonicalData(args, "$.argv"); + if (!Array.isArray(args) || nodeUtilTypes4.isProxy(args) || args.some((arg) => typeof arg !== "string")) { + throw new Error(`${bossClient} arguments must be a plain dense string array`); + } + } + if (bossClient === void 0) return deriveBridgeAgentRuntimeConfig(args, cwd); + let afterSeparator = false; + for (const arg of args) { + if (arg === "--") { + afterSeparator = true; + continue; + } + const attachedAuthorityOption = hardenedBossAttachedAuthorityOption(arg); + if (attachedAuthorityOption === "-C") { + throw new Error(`${bossClient} cannot expand or replace its writable root`); + } + if (attachedAuthorityOption !== void 0) { + throw new Error(`${bossClient} cannot use attached ${attachedAuthorityOption} policy overrides`); + } + const optionName = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg; + if (isHardenedBossRawConfigOption(optionName)) { + throw new Error(`${bossClient} cannot use raw ${optionName} or profile configuration`); + } + if (HARDENED_BOSS_BYPASS_OPTIONS.has(optionName)) { + throw new Error(`${bossClient} cannot use approval or sandbox bypass aliases`); + } + if (optionName === "--add-dir" || optionName === "--cd" || optionName === "-C") { + throw new Error(`${bossClient} cannot expand or replace its writable root`); + } + if (afterSeparator && ["--sandbox", "-s", "--ask-for-approval", "-a"].includes(optionName)) { + throw new Error(`${bossClient} cannot place policy overrides after the argument separator`); + } + if (optionName.startsWith("-") && /(?:yolo|danger|bypass)/i.test(optionName)) { + throw new Error(`${bossClient} cannot use approval or sandbox bypass aliases`); + } + } + if (bossClient === "boss_participant") { + throw new Error("boss_participant requires unavailable broker-owned assigned workspace authority"); + } + const runtime = deriveBridgeAgentRuntimeConfig(args, cwd); + const probe = { + id: "launch-validation", + name: "launch-validation", + cwd: resolve4(cwd), + bossClient, + ...runtime + }; + assertHardenedBossAgentConfig(probe); + return runtime; +} function parseCoiArgs(argv, env = process.env) { const codexArgs = []; const options = {}; @@ -3389,6 +4243,7 @@ function parseCoiArgs(argv, env = process.env) { } if (arg === "--") { afterSeparator = true; + codexArgs.push(arg); continue; } const [key, inlineValue] = arg.includes("=") ? arg.split(/=(.*)/s, 2) : [arg, void 0]; @@ -3448,7 +4303,7 @@ function parseCoiArgs(argv, env = process.env) { }; } function hasCodexHelpOrVersion(args) { - return args.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V"); + return splitCodexResumeArgs(args).optionArgs.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V"); } function splitCodexResumeArgs(args) { const optionArgs = []; @@ -3458,7 +4313,7 @@ function splitCodexResumeArgs(args) { const arg = args[index]; if (arg === "--") { promptArgs.push(...args.slice(index + 1)); - return { optionArgs, promptArgs }; + return { optionArgs, promptArgs, separatorPresent: true }; } if (!arg.startsWith("-") || arg === "-") break; optionArgs.push(arg); @@ -3469,15 +4324,16 @@ function splitCodexResumeArgs(args) { } } promptArgs.push(...args.slice(index)); - return { optionArgs, promptArgs }; + return { optionArgs, promptArgs, separatorPresent: false }; } function resolveCoiResumeRequest(args) { - const { optionArgs, promptArgs } = splitCodexResumeArgs(args); - if (promptArgs[0] !== "resume" || !promptArgs[1]) return { optionArgs, promptArgs }; + const { optionArgs, promptArgs, separatorPresent } = splitCodexResumeArgs(args); + if (separatorPresent || promptArgs[0] !== "resume" || !promptArgs[1]) return { optionArgs, promptArgs }; return { optionArgs, threadId: promptArgs[1], promptArgs: promptArgs.slice(2) }; } function buildCoiTuiArgs(remote, optionArgs, threadId, promptArgs, explicitResume) { - return explicitResume ? ["resume", "--remote", remote, ...optionArgs, threadId, ...promptArgs] : ["--remote", remote, ...optionArgs, ...promptArgs]; + const promptTail = promptArgs.length === 0 ? [] : ["--", ...promptArgs]; + return explicitResume ? ["resume", "--remote", remote, ...optionArgs, threadId, ...promptTail] : ["--remote", remote, ...optionArgs, ...promptTail]; } function buildCodexAppServerArgs(args, socketPath, env = process.env) { const { optionArgs } = splitCodexResumeArgs(args); @@ -3510,7 +4366,7 @@ async function waitForSocket(socketPath, proc, timeoutMs = 1e4) { if (proc.exitCode !== null) { throw new Error(`Codex app-server exited before creating ${socketPath}`); } - if (existsSync5(socketPath)) return; + if (existsSync6(socketPath)) return; await delay2(50); } throw new Error(`Timed out waiting for Codex app-server socket: ${socketPath}`); @@ -3535,9 +4391,10 @@ function terminalNotification(message) { else process.stderr.write(`${safe} `); } -async function runInteractiveTui(command, args, refreshArgs, cwd, onAltI, onAltM, installRefresh) { +async function runInteractiveTui(command, args, refreshArgs, cwd, onAltI, onAltM, installRefresh, protectedBossClient, launchEnv = process.env) { + assertHardenedBossProviderAuthority(protectedBossClient); const runInherited = async () => { - const tui2 = spawn4(command, args, { cwd, env: process.env, stdio: "inherit" }); + const tui2 = spawn4(command, args, { cwd, env: launchEnv, stdio: "inherit" }); const [code, signal] = await once2(tui2, "exit"); if (typeof code === "number") return code; return signal === "SIGINT" ? 130 : 1; @@ -3554,11 +4411,11 @@ async function runInteractiveTui(command, args, refreshArgs, cwd, onAltI, onAltM return runInherited(); } const tui = nodePty.spawn(command, args, { - name: process.env.TERM || "xterm-256color", + name: launchEnv.TERM || "xterm-256color", cols: process.stdout.columns || 80, rows: process.stdout.rows || 24, cwd, - env: process.env + env: launchEnv }); const outputSubscription = tui.onData((data) => process.stdout.write(data)); let refreshRequested = false; @@ -3621,7 +4478,7 @@ async function runInteractiveTui(command, args, refreshArgs, cwd, onAltI, onAltM outputSubscription.dispose(); } if (refreshRequested) { - return runInteractiveTui(command, refreshArgs, refreshArgs, cwd, onAltI, onAltM, installRefresh); + return runInteractiveTui(command, refreshArgs, refreshArgs, cwd, onAltI, onAltM, installRefresh, protectedBossClient, launchEnv); } return exitCode; } @@ -3634,7 +4491,7 @@ function cleanupOldCoiStateFiles(intercomDir, now = Date.now(), maxAgeMs = COI_S } for (const entry of entries) { if (!/^coi-.+-state\.json$/.test(entry)) continue; - const path = join7(intercomDir, entry); + const path = join8(intercomDir, entry); try { const stat = statSync(path); if (now - stat.mtimeMs > maxAgeMs) rmSync(path, { force: true }); @@ -3645,11 +4502,17 @@ function cleanupOldCoiStateFiles(intercomDir, now = Date.now(), maxAgeMs = COI_S function resetCoiStateForFreshStart(statePath, fresh) { if (fresh) rmSync(statePath, { force: true }); } -async function runCoi(options) { +async function runCoi(options, env = process.env) { + const bossClient = parseHardenedBossClientKind(env.CODEX_INTERCOM_BOSS_CLIENT?.trim(), "CODEX_INTERCOM_BOSS_CLIENT"); + assertHardenedBossProviderAuthority(bossClient); + if (bossClient !== void 0 && options.codexCommand !== "codex") { + throw new Error(`${bossClient} cannot use a caller-provided Codex/app-server command`); + } + const runtimeConfig = assertHardenedBossCoiLaunch(options.codexArgs, options.cwd, bossClient); if (hasCodexHelpOrVersion(options.codexArgs)) { const help = spawn4(options.codexCommand, options.codexArgs, { cwd: options.cwd, - env: process.env, + env, stdio: "inherit" }); const [code, signal] = await once2(help, "exit"); @@ -3662,18 +4525,30 @@ async function runCoi(options) { const name = options.name ?? identity.name; const intercomDir = getIntercomDirPath(); cleanupOldCoiStateFiles(intercomDir); - const socketPath = options.socketPath ?? join7(intercomDir, `coi-${process.pid}.sock`); - const statePath = options.statePath ?? join7(intercomDir, `coi-${sanitizeSegment(id)}-state.json`); - const fresh = process.env.AGENT_INTERCOM_FRESH === "1"; + const socketPath = options.socketPath ?? join8(intercomDir, `coi-${process.pid}.sock`); + const statePath = options.statePath ?? join8(intercomDir, `coi-${sanitizeSegment(id)}-state.json`); + const fresh = env.AGENT_INTERCOM_FRESH === "1"; resetCoiStateForFreshStart(statePath, fresh); rmSync(socketPath, { force: true }); - const appServer = spawn4(options.codexCommand, buildCodexAppServerArgs(options.codexArgs, socketPath), { + const resumeRequest = resolveCoiResumeRequest(options.codexArgs); + const agent = { + id, + name, + cwd: options.cwd, + model: env.CODEX_INTERCOM_MODEL, + instructions: options.instructions, + threadId: fresh ? void 0 : resumeRequest.threadId, + ...bossClient === void 0 ? {} : { bossClient }, + ...runtimeConfig + }; + assertHardenedBossAgentConfig(agent); + const appServer = spawn4(options.codexCommand, buildCodexAppServerArgs(options.codexArgs, socketPath, env), { cwd: options.cwd, - env: process.env, + env, stdio: ["ignore", "ignore", "pipe"] }); appServer.stderr?.on("data", (chunk) => { - if (process.env.CODEX_INTERCOM_DEBUG) process.stderr.write(String(chunk)); + if (env.CODEX_INTERCOM_DEBUG) process.stderr.write(String(chunk)); }); const cleanup = async () => { await daemon?.stop().catch(() => void 0); @@ -3694,22 +4569,13 @@ async function runCoi(options) { void cleanupOnce().finally(() => process.exit(143)); }); await waitForSocket(socketPath, appServer); - const resumeRequest = resolveCoiResumeRequest(options.codexArgs); const config = { statePath, appServer: { transport: "unix-websocket", socketPath }, - agents: [{ - id, - name, - cwd: options.cwd, - model: process.env.CODEX_INTERCOM_MODEL, - instructions: options.instructions, - threadId: fresh ? void 0 : resumeRequest.threadId, - ...deriveBridgeAgentRuntimeConfig(options.codexArgs, options.cwd) - }] + agents: [agent] }; let refreshVisibleTui; daemon = new CodexBridgeDaemon(config, { @@ -3745,7 +4611,7 @@ async function runCoi(options) { copying = true; void daemon.getContactTargetForAgent(id).then(async (contact) => { const instruction = formatContactInstruction(contact); - const preferTerminal = Boolean(process.env.SSH_TTY || process.env.SSH_CONNECTION); + const preferTerminal = Boolean(env.SSH_TTY || env.SSH_CONNECTION); let copied = preferTerminal ? copyTextToTerminalClipboard(instruction, (sequence) => process.stdout.write(sequence)) : await copyTextToClipboard(instruction); if (!copied.ok && process.stdout.isTTY) { copied = copyTextToTerminalClipboard(instruction, (sequence) => process.stdout.write(sequence)); @@ -3778,7 +4644,9 @@ async function runCoi(options) { return () => { if (refreshVisibleTui === refresh) refreshVisibleTui = void 0; }; - } + }, + bossClient, + env ); } finally { await cleanupOnce(); @@ -3797,6 +4665,7 @@ if (process.argv[1] && (basename2(process.argv[1]) === "coi.ts" || basename2(pro }); } export { + assertHardenedBossCoiLaunch, buildCodexAppServerArgs, buildCoiTuiArgs, cleanupOldCoiStateFiles, @@ -3807,6 +4676,7 @@ export { resetCoiStateForFreshStart, resolveCoiResumeRequest, runCoi, + runInteractiveTui, sanitizeSegment, splitCodexResumeArgs }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e35e773 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,614 @@ +{ + "name": "@dataforxyz/agent-intercom-codex", + "version": "0.10.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@dataforxyz/agent-intercom-codex", + "version": "0.10.0", + "license": "AGPL-3.0-or-later", + "bin": { + "codex-intercom-bridge": "dist/bridge-daemon.mjs", + "codex-intercom-mcp": "dist/codex-server.mjs", + "coi": "dist/coi.mjs" + }, + "devDependencies": { + "@dataforxyz/agent-intercom-core": "git+https://github.com/dataforxyz/agent-intercom-core.git#8316cbab548f422ad11c78ed887fabeef94817c1", + "@types/node": "^24.0.0", + "esbuild": "^0.28.1", + "tsx": "^4.20.0", + "typescript": "^6.0.3" + }, + "engines": { + "node": ">=22.19.0" + }, + "optionalDependencies": { + "node-pty": "^1.1.0" + }, + "peerDependencies": { + "@dataforxyz/agent-intercom-core": "0.1.0" + } + }, + "node_modules/@dataforxyz/agent-intercom-core": { + "version": "0.1.0", + "resolved": "git+https://github.com/dataforxyz/agent-intercom-core.git#8316cbab548f422ad11c78ed887fabeef94817c1", + "integrity": "sha512-tGEdYHG/Zrl/VSkOQMpjZ8LgnxG4O7youbVOEjZVwG+XbjhLVkDOeODa+U3plIUwbCVIXJHodmbJuH0yW5SIRA==", + "dev": true, + "license": "AGPL-3.0-or-later", + "dependencies": { + "@types/node": "^24.10.0" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/node-pty": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", + "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^7.1.0" + } + }, + "node_modules/tsx": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index 6615786..d5a3cd7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "AGPL-3.0-or-later", "type": "module", "engines": { - "node": ">=20" + "node": ">=22.19.0" }, "main": "dist/codex-server.mjs", "bin": { @@ -21,11 +21,13 @@ "broker/**/*.ts", "!broker/**/*.test.ts", "scripts/**/*", + "!scripts/**/*.test.mjs", "skills/**/*", "types.ts", "config.ts", "tsconfig.json", "durable-json.ts", + "boss-control-outbox.ts", "outbound-outbox.ts", ".codex-plugin/**/*", ".mcp.json", @@ -42,7 +44,7 @@ "codex:mcp": "tsx codex/server.ts", "codex:bridge": "tsx codex/bridge-daemon.ts", "coi": "tsx codex/coi.ts", - "test": "tsx --test broker/*.test.ts outbound-outbox.test.ts codex/*.test.ts scripts/*.test.mjs", + "test": "node --import tsx --test --test-isolation=none broker/*.test.ts boss-control-outbox.test.ts outbound-outbox.test.ts codex/*.test.ts scripts/*.test.mjs", "typecheck": "tsc --noEmit" }, "keywords": [ @@ -52,13 +54,14 @@ "multi-agent", "pi" ], - "dependencies": { - "@dataforxyz/agent-intercom-core": "git+https://github.com/dataforxyz/agent-intercom-core.git#cb5d2212912db0cd8abbb16ab08e4b539424a05d" + "peerDependencies": { + "@dataforxyz/agent-intercom-core": "0.1.0" }, "optionalDependencies": { "node-pty": "^1.1.0" }, "devDependencies": { + "@dataforxyz/agent-intercom-core": "git+https://github.com/dataforxyz/agent-intercom-core.git#8316cbab548f422ad11c78ed887fabeef94817c1", "@types/node": "^24.0.0", "esbuild": "^0.28.1", "tsx": "^4.20.0", diff --git a/scripts/boss-remediation-dist.test.mjs b/scripts/boss-remediation-dist.test.mjs new file mode 100644 index 0000000..719b2e4 --- /dev/null +++ b/scripts/boss-remediation-dist.test.mjs @@ -0,0 +1,172 @@ +import assert from "node:assert/strict"; +import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; +import { CodexBridgeDaemon } from "../dist/bridge-daemon.mjs"; +import { + assertHardenedBossCoiLaunch, + buildCoiTuiArgs, + hasCodexHelpOrVersion, + resolveCoiResumeRequest, + runCoi, + runInteractiveTui, +} from "../dist/coi.mjs"; + +test("built coi preserves separator and enforces the same hostile argv ceiling", () => { + assert.deepEqual(buildCoiTuiArgs("unix:///tmp/coi.sock", [], "thread-1", ["--literal"], false), [ + "--remote", "unix:///tmp/coi.sock", "--", "--literal", + ]); + assert.equal(hasCodexHelpOrVersion(["--", "--help"]), false); + assert.deepEqual(resolveCoiResumeRequest(["--", "resume", "attacker-thread"]), { + optionArgs: [], + promptArgs: ["resume", "attacker-thread"], + }); + assert.throws(() => assertHardenedBossCoiLaunch(["--", "--yolo"], "/tmp/project", "boss_participant")); + for (const arg of ["-sworkspace-write", "-anever", "-C/etc"]) { + assert.throws(() => assertHardenedBossCoiLaunch([arg], "/tmp/project", "boss_reviewer")); + assert.throws(() => assertHardenedBossCoiLaunch(["--", arg], "/tmp/project", "boss_reviewer")); + } + assert.deepEqual(assertHardenedBossCoiLaunch([], "/tmp/project", "boss_reviewer"), {}); + assert.deepEqual( + assertHardenedBossCoiLaunch(["-s", "read-only", "-a", "untrusted"], "/tmp/project", "boss_reviewer"), + { approvalPolicy: "untrusted", sandboxPolicy: { type: "readOnly", networkAccess: false } }, + ); + assert.throws( + () => assertHardenedBossCoiLaunch(["--sandbox=workspace-write"], "/tmp/project", "boss_participant"), + /broker-owned assigned workspace authority/, + ); +}); + +test("built bridge returns provider authority unavailable before config or connect", () => { + const agent = { + id: "worker", + name: "worker", + cwd: "/tmp/project", + bossClient: "boss_participant", + }; + const unavailable = (error) => error?.code === "PROVIDER_AUTHORITY_UNAVAILABLE"; + assert.throws(() => new CodexBridgeDaemon({ + statePath: "/tmp/state", + agents: [{ ...agent, sandboxPolicy: { type: "workspaceWrite", writableRoots: ["/tmp/project"], networkAccess: true } }], + }), unavailable); + assert.throws(() => new CodexBridgeDaemon({ + statePath: "/tmp/state", + agents: [agent], + appServer: { command: "/attacker/codex" }, + }), unavailable); + assert.throws(() => new CodexBridgeDaemon({ + statePath: "/tmp/state", + agents: [agent], + }), unavailable); + + const reviewer = { + id: "reviewer", + name: "reviewer", + cwd: "/tmp", + bossClient: "boss_reviewer", + sandboxPolicy: { type: "readOnly", networkAccess: false }, + }; + for (const field of ["args", "startDaemonArgs"]) { + for (const argv of [ + ["app-server", "--sandbox=workspace-write"], + ["app-server", "--", "-a=never"], + ["app-server", "-C/etc"], + ["app-server", "--", "-C/etc"], + ]) { + assert.throws(() => new CodexBridgeDaemon({ + statePath: "/tmp/state", + agents: [reviewer], + appServer: { [field]: argv }, + }), unavailable); + } + } +}); + +test("built protected entry points never execute hostile PATH Codex and ordinary explicit TUI remains available", async () => { + const dir = mkdtempSync(join(tmpdir(), "codex-provider-dist-")); + const marker = join(dir, "executed"); + const hostile = join(dir, "codex"); + const explicit = join(dir, "explicit-codex"); + writeFileSync(hostile, `#!/bin/sh\nprintf hostile > '${marker}'\n`); + writeFileSync(explicit, "#!/bin/sh\nprintf ordinary > \"$1\"\n"); + chmodSync(hostile, 0o755); + chmodSync(explicit, 0o755); + const hostileEnv = { ...process.env, PATH: dir, CODEX_INTERCOM_BOSS_CLIENT: "boss_reviewer" }; + const unavailable = (error) => error?.code === "PROVIDER_AUTHORITY_UNAVAILABLE"; + try { + for (const options of [ + { cwd: dir, noTui: false, copyShortcut: false, codexCommand: "codex", codexArgs: ["--help"] }, + { cwd: dir, noTui: true, copyShortcut: false, codexCommand: "codex", codexArgs: [] }, + { cwd: dir, noTui: false, copyShortcut: false, codexCommand: "codex", codexArgs: [] }, + ]) { + await assert.rejects(runCoi(options, hostileEnv), unavailable); + assert.equal(existsSync(marker), false); + } + assert.throws(() => new CodexBridgeDaemon({ + statePath: join(dir, "state.json"), + agents: [{ id: "reviewer", name: "reviewer", cwd: dir, bossClient: "boss_reviewer" }], + }), unavailable); + assert.equal(existsSync(marker), false); + + await assert.rejects( + runInteractiveTui("codex", ["initial"], ["resume", "thread-1"], dir, undefined, undefined, undefined, "boss_reviewer", hostileEnv), + (error) => error?.code === "PROVIDER_AUTHORITY_UNAVAILABLE", + ); + assert.equal(existsSync(marker), false); + assert.equal(await runInteractiveTui(explicit, [marker], [marker], dir), 0); + assert.equal(readFileSync(marker, "utf8"), "ordinary"); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("every relevant built entry contains the exact CX3 state-machine and roster closures", () => { + const broker = readFileSync(new URL("../dist/broker.mjs", import.meta.url), "utf8"); + const clients = ["codex-server.mjs", "bridge-daemon.mjs", "coi.mjs"].map((name) => ( + readFileSync(new URL(`../dist/${name}`, import.meta.url), "utf8") + )); + assert.match(broker, /function bossControlReplayFrames/); + assert.match(broker, /function bossControlAcceptedRecoveryFrames/); + assert.match(broker, /for \(const frame of bossControlAcceptedRecoveryFrames\(result\)\)/); + assert.match(broker, /recordAccepted\(key, fingerprint, deliveryId/); + assert.match(broker, /recordAccepted\(scope, fingerprint, deliveryId\)/); + assert.doesNotMatch(broker, /recordAccepted\(scope, fingerprint, deliveryId, ttlMs\)/); + assert.match(broker, /var RECENT_DELIVERY_TTL_MS = 10 \* 60 \* 1e3/); + assert.match(broker, /pruneRecentDeliveries\(now = Date\.now\(\)\)/); + assert.match(broker, /expiresAt: Date\.now\(\) \+ RECENT_DELIVERY_TTL_MS/); + assert.match(broker, /Boss control ledger was corrupt and quarantined/); + assert.doesNotMatch(broker, /registrationKind:\s*["']ordinary["']/); + for (const client of clients) { + assert.match(client, /removeCorrelated\(idempotencyKey, messageId, deliveryId\)/); + assert.match(client, /markAccepted\(idempotencyKey, messageId, deliveryId\)/); + assert.match(client, /pending\.deliveryId !== deliveryId/); + assert.doesNotMatch(client, /Duplicate Boss control acknowledgement/); + assert.match(client, /bossControlKind\(brokerMessage\.envelope\)\.envelope/); + assert.match(client, /Boss control outbox was corrupt and quarantined/); + assert.match(client, /function exactBossRosterSession/); + assert.match(client, /currentRole === "manager" \|\| currentRole === "controller"/); + assert.match(client, /worker\.canonicalIdentity\.bossRunId === bossIdentity\.bossRunId/); + } +}); + +test("built target list omits the unprotected restricted-client bundle", () => { + const info = JSON.parse(readFileSync(new URL("../dist/build-info.json", import.meta.url), "utf8")); + assert.deepEqual(info.targets, ["codex-server", "broker", "bridge-daemon", "coi"]); + assert.equal(existsSync(new URL("../dist/boss-client.mjs", import.meta.url)), false); +}); + +test("built and packaged surfaces use the exact runtime Core peer without an embedded duplicate", () => { + const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")); + assert.equal(packageJson.peerDependencies["@dataforxyz/agent-intercom-core"], "0.1.0"); + assert.equal( + packageJson.devDependencies["@dataforxyz/agent-intercom-core"], + "git+https://github.com/dataforxyz/agent-intercom-core.git#8316cbab548f422ad11c78ed887fabeef94817c1", + ); + + for (const name of ["codex-server.mjs", "broker.mjs", "bridge-daemon.mjs", "coi.mjs"]) { + const built = readFileSync(new URL(`../dist/${name}`, import.meta.url), "utf8"); + assert.match(built, /from "@dataforxyz\/agent-intercom-core(?:\/[^"]+)?"/); + assert.doesNotMatch(built, /node_modules\/@dataforxyz\/agent-intercom-core\//); + } +}); diff --git a/scripts/build-info.mjs b/scripts/build-info.mjs index e75a2f0..55e74d9 100644 --- a/scripts/build-info.mjs +++ b/scripts/build-info.mjs @@ -8,6 +8,7 @@ const RUNTIME_ROOTS = [ ]; const RUNTIME_FILES = [ "config.ts", + "boss-control-outbox.ts", "durable-json.ts", "outbound-outbox.ts", "package.json", diff --git a/scripts/build-info.test.mjs b/scripts/build-info.test.mjs index c235c14..5cceda2 100644 --- a/scripts/build-info.test.mjs +++ b/scripts/build-info.test.mjs @@ -13,7 +13,7 @@ async function sourceFixture() { await writeFile(join(root, "broker", "client.ts"), "export const client = 1;\n"); await writeFile(join(root, "broker", "client.test.ts"), "ignored test\n"); await writeFile(join(root, "codex", "coi.ts"), "export const coi = 1;\n"); - for (const file of ["config.ts", "durable-json.ts", "outbound-outbox.ts", "types.ts"]) { + for (const file of ["config.ts", "boss-control-outbox.ts", "durable-json.ts", "outbound-outbox.ts", "types.ts"]) { await writeFile(join(root, file), `${file}\n`); } await writeFile(join(root, "package.json"), "{}\n"); diff --git a/scripts/build.mjs b/scripts/build.mjs index 4cface6..3328146 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -14,8 +14,12 @@ const common = { bundle: true, platform: "node", format: "esm", - target: "node20", + target: "node22.19", }; +const coreExternals = [ + "@dataforxyz/agent-intercom-core", + "@dataforxyz/agent-intercom-core/*", +]; const outputs = [ { entry: "codex/server.ts", outfile: "dist/codex-server.mjs", target: "codex-server", executable: true }, { entry: "broker/broker.ts", outfile: "dist/broker.mjs", target: "broker", executable: false }, @@ -28,7 +32,7 @@ await Promise.all(outputs.map((output) => build({ entryPoints: [output.entry], outfile: output.outfile, banner: { js: buildBanner(identity(output.target), output.executable ? "#!/usr/bin/env node" : "") }, - ...(output.external ? { external: output.external } : {}), + external: [...coreExternals, ...(output.external ?? [])], }))); await writeFile("dist/build-info.json", `${JSON.stringify({ diff --git a/types.ts b/types.ts index 794891a..876ce0f 100644 --- a/types.ts +++ b/types.ts @@ -1,3 +1,30 @@ +import type { + BossControlEnvelope, + BossParticipantBinding, + BossParticipantCredentialEnvelope, + BossRunFeatureContract, + BrokerCapabilityAdvertisement, + ParticipantState, + WorkerIdentityV2, +} from "@dataforxyz/agent-intercom-core/boss"; + +export interface BossParticipantRegistrationMetadata { + featureContract: BossRunFeatureContract; + credential: BossParticipantCredentialEnvelope; +} + +/** Broker-owned metadata. Clients may request Boss registration but never assert a binding. */ +export interface BossParticipantBindingMetadata { + featureContract: BossRunFeatureContract; + binding: BossParticipantBinding; + brokerIdentityVerified: true; + assignedParticipantIds?: string[]; + requestingPrincipalId?: string; + /** Required by Boss roster projection before a session is discoverable. */ + workerIdentity?: WorkerIdentityV2; + participantState?: ParticipantState; +} + export interface SessionInfo { id: string; name?: string; @@ -18,6 +45,7 @@ export interface SessionInfo { depth?: number; maxDepth?: number; maxChildren?: number; + boss?: BossParticipantBindingMetadata; } export interface Message { @@ -38,14 +66,25 @@ export interface Attachment { language?: string; } -export type SessionRegistration = Omit< +type SessionRegistrationBase = Omit< SessionInfo, - "id" | "peerUid" | "trustedLocal" | "origin" | "remoteHostId" | "parentSessionId" | "rootSessionId" | "generation" | "canDelegate" | "depth" | "maxDepth" | "maxChildren" + "id" | "peerUid" | "trustedLocal" | "origin" | "remoteHostId" | "parentSessionId" | "rootSessionId" | "generation" | "canDelegate" | "depth" | "maxDepth" | "maxChildren" | "boss" > & { /** Ephemeral identity shared only by reconnects from one live runtime. */ runtimeInstanceId?: string; }; +export type OrdinarySessionRegistration = SessionRegistrationBase & { + boss?: never; +}; + +export type BossSessionRegistration = SessionRegistrationBase & { + /** Optional untrusted request; only a protected, attested broker may return a binding. */ + boss: BossParticipantRegistrationMetadata; +}; + +export type SessionRegistration = OrdinarySessionRegistration | BossSessionRegistration; + export interface RemoteEnrollmentAccess { enrollmentToken: string; } @@ -112,12 +151,22 @@ export type DeliveryFailureCode = | "SENDER_DISCONNECTED" | "DELIVERY_TIMEOUT"; +export type BossControlFailureCode = + | "INVALID_CONTROL" + | "IDEMPOTENCY_CONFLICT" + | "SESSION_NOT_FOUND" + | "POLICY_DENIED" + | "RECIPIENT_DISCONNECTED" + | "DELIVERY_TIMEOUT"; + export type BrokerErrorCode = | "PROTOCOL_MISMATCH" | "INVALID_REQUEST" | "SESSION_ID_IN_USE" | "ACCESS_DENIED" | "REMOTE_ACCESS_INCOMPATIBLE" + | "BOSS_FEATURE_UNAVAILABLE" + | "BOSS_CONTRACT_MISMATCH" | "RATE_LIMITED" | "TOO_MANY_SESSIONS"; @@ -130,7 +179,8 @@ export type AskCancellationReason = export type ClientMessage = | { type: "health"; requestId: string; stateId?: string } - | { type: "register"; protocol: string; version: number; session: SessionRegistration; sessionId?: string; stateId?: string; access?: RemoteRegistrationAccess } + | { type: "register"; protocol: string; version: number; session: OrdinarySessionRegistration; sessionId?: string; stateId?: string; access?: RemoteRegistrationAccess } + | { type: "register"; registrationKind: "boss"; protocol: string; version: number; session: BossSessionRegistration; sessionId?: string; stateId?: string; access?: never } | { type: "access_control"; requestId: string; adminToken: string; action: "issue_enrollment"; enrollment: { name: string; parentSessionId: string; rootSessionId: string; remoteHostId: string; ttlMs?: number; expiresAt?: number; canDelegate?: boolean; maxDepth?: number; maxChildren?: number } } | { type: "access_control"; requestId: string; adminToken: string; action: "revoke_subtree"; principalId: string } | { type: "access_control"; requestId: string; adminToken: string; action: "inspect_tree"; principalId: string } @@ -140,6 +190,8 @@ export type ClientMessage = | { type: "unregister"; preserveAsks?: boolean } | { type: "list"; requestId: string } | { type: "send"; to: string; message: Message } + | { type: "boss_control"; requestId: string; to: string; envelope: BossControlEnvelope } + | { type: "boss_control_received"; deliveryId: string; messageId: string; idempotencyKey: string } | { type: "message_received"; deliveryId: string } | { type: "message_rejected"; deliveryId: string; code: "CONFLICTING_MESSAGE_ID"; reason: string } | { type: "defer_ask"; requestId: string; messageId: string } @@ -147,8 +199,9 @@ export type ClientMessage = | { type: "presence"; name?: string; status?: string; model?: string }; export type BrokerMessage = - | { type: "health_ok"; requestId: string; protocol: string; version: number; endpoint: "local" | "remote"; remoteAccess?: RemoteAccessContract } + | { type: "health_ok"; requestId: string; protocol: string; version: number; endpoint: "local" | "remote"; remoteAccess?: RemoteAccessContract; capabilities?: BrokerCapabilityAdvertisement } | { type: "registered"; sessionId: string; protocol: string; version: number; remoteAccess?: RemoteAccessContract; access?: RemoteAccessMetadata } + | { type: "registered"; registrationKind: "boss"; sessionId: string; protocol: string; version: number; capabilities: BrokerCapabilityAdvertisement; boss: BossParticipantBindingMetadata; remoteAccess?: never; access?: never } | { type: "access_control_result"; requestId: string; action: "issue_enrollment"; enrollmentToken: string; expiresAt: number } | { type: "access_control_result"; requestId: string; action: "revoke_subtree"; changedPrincipalIds: string[] } | { type: "access_control_result"; requestId: string; action: "inspect_tree"; principals: RemotePrincipalSummary[] } @@ -156,6 +209,10 @@ export type BrokerMessage = | { type: "access_control_result"; requestId: string; action: "issue_child_enrollment"; enrollmentToken: string; expiresAt: number; parentSessionId: string } | { type: "sessions"; requestId: string; sessions: SessionInfo[] } | { type: "message"; deliveryId: string; from: SessionInfo; message: Message } + | { type: "boss_control"; deliveryId: string; from: SessionInfo; envelope: BossControlEnvelope } + | { type: "boss_control_ack"; requestId: string; messageId: string; idempotencyKey: string; status: "accepted"; deliveryId: string } + | { type: "boss_control_result"; requestId: string; messageId: string; idempotencyKey: string; status: "delivered"; delivered: true; deliveryId: string } + | { type: "boss_control_result"; requestId: string; messageId: string; idempotencyKey: string; status: "rejected"; delivered: false; deliveryId?: string; code: BossControlFailureCode; reason: string } | { type: "presence_update"; session: SessionInfo } | { type: "session_joined"; session: SessionInfo } | { type: "session_left"; sessionId: string } From 22f1273ca4337a27205866b15ded8f74cf3829dc Mon Sep 17 00:00:00 2001 From: Ben U Date: Wed, 29 Jul 2026 17:57:32 -0600 Subject: [PATCH 2/2] ci: support minimum Node test runner --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5a3cd7..084d73d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "codex:mcp": "tsx codex/server.ts", "codex:bridge": "tsx codex/bridge-daemon.ts", "coi": "tsx codex/coi.ts", - "test": "node --import tsx --test --test-isolation=none broker/*.test.ts boss-control-outbox.test.ts outbound-outbox.test.ts codex/*.test.ts scripts/*.test.mjs", + "test": "node --import tsx --test broker/*.test.ts boss-control-outbox.test.ts outbound-outbox.test.ts codex/*.test.ts scripts/*.test.mjs", "typecheck": "tsc --noEmit" }, "keywords": [