feat(p9): route OutboundService through the Capability Broker
Task 9.3: OutboundService now delegates the execute step to CapabilityBroker (policy-gated + obligation-enforced) instead of calling a provider directly, while keeping idempotency + rate-limit + the command/attempt ledger. BLOCKED obligations → command BLOCKED; a fail-closed throw marks the command FAILED then propagates. AdaptersModule imports CapabilityModule; P4/P6/P8 egress now flows through the broker unchanged. Updated 6 spec constructions; added a DENY_OUTBOUND test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ import { ActorResolver } from '../identity/actor.resolver';
|
||||
import { AiJobService } from '../ai/ai.service';
|
||||
import { AiBudgetGuard } from '../ai/ai-budget.guard';
|
||||
import { OutboundService } from '../adapters/outbound.service';
|
||||
import { AdapterRegistry } from '../adapters/adapter.registry';
|
||||
import { CapabilityBroker } from '../capability/capability.broker';
|
||||
import { CapabilityProviderRegistry } from '../capability/capability.registry';
|
||||
import { CalendarService } from './calendar.service';
|
||||
import type { PrismaService } from '../prisma/prisma.service';
|
||||
import type { FakePorts } from '@insignia/iios-testkit';
|
||||
@@ -21,7 +22,7 @@ const alice: MessagePrincipal = { userId: 'alice', orgId: 'org_demo', appId: 'po
|
||||
const START = '2026-07-02T10:00:00.000Z';
|
||||
|
||||
const ai = () => new AiJobService(asService, makeFakePorts(), makeFakeInference(), new AiBudgetGuard(asService), actors);
|
||||
const cal = (ports?: FakePorts) => new CalendarService(asService, ports ?? makeFakePorts(), actors, ai(), new OutboundService(asService, new AdapterRegistry()));
|
||||
const cal = (ports?: FakePorts) => new CalendarService(asService, ports ?? makeFakePorts(), actors, ai(), new OutboundService(asService, new CapabilityBroker(makeFakePorts(), new CapabilityProviderRegistry())));
|
||||
|
||||
async function scheduledMeeting(attendees: { userId: string; visibility?: 'FULL' | 'NONE' }[]) {
|
||||
const m = await cal().scheduleDirect(alice, { meetingType: 'INTERNAL', title: 'Board sync', startAt: START, attendees });
|
||||
|
||||
@@ -9,7 +9,8 @@ import { ActorResolver } from '../identity/actor.resolver';
|
||||
import { AiJobService } from '../ai/ai.service';
|
||||
import { AiBudgetGuard } from '../ai/ai-budget.guard';
|
||||
import { OutboundService } from '../adapters/outbound.service';
|
||||
import { AdapterRegistry } from '../adapters/adapter.registry';
|
||||
import { CapabilityBroker } from '../capability/capability.broker';
|
||||
import { CapabilityProviderRegistry } from '../capability/capability.registry';
|
||||
import { CalendarService } from './calendar.service';
|
||||
import { CalendarProjector } from './calendar.projector';
|
||||
import { OutboxBus } from '../outbox/outbox.bus';
|
||||
@@ -24,7 +25,7 @@ const alice: MessagePrincipal = { userId: 'alice', orgId: 'org_demo', appId: 'po
|
||||
const START = '2026-07-02T10:00:00.000Z';
|
||||
|
||||
const ai = () => new AiJobService(asService, makeFakePorts(), makeFakeInference(), new AiBudgetGuard(asService), actors);
|
||||
const cal = () => new CalendarService(asService, makeFakePorts(), actors, ai(), new OutboundService(asService, new AdapterRegistry()));
|
||||
const cal = () => new CalendarService(asService, makeFakePorts(), actors, ai(), new OutboundService(asService, new CapabilityBroker(makeFakePorts(), new CapabilityProviderRegistry())));
|
||||
|
||||
async function makeInteraction(text: string): Promise<string> {
|
||||
const m = new MessageService(asService, makeFakePorts(), actors);
|
||||
|
||||
@@ -9,7 +9,8 @@ import { ActorResolver } from '../identity/actor.resolver';
|
||||
import { AiJobService } from '../ai/ai.service';
|
||||
import { AiBudgetGuard } from '../ai/ai-budget.guard';
|
||||
import { OutboundService } from '../adapters/outbound.service';
|
||||
import { AdapterRegistry } from '../adapters/adapter.registry';
|
||||
import { CapabilityBroker } from '../capability/capability.broker';
|
||||
import { CapabilityProviderRegistry } from '../capability/capability.registry';
|
||||
import { CalendarService } from './calendar.service';
|
||||
import type { PrismaService } from '../prisma/prisma.service';
|
||||
import type { FakePorts } from '@insignia/iios-testkit';
|
||||
@@ -23,7 +24,7 @@ const alice: MessagePrincipal = { userId: 'alice', orgId: 'org_demo', appId: 'po
|
||||
const START = '2026-07-02T10:00:00.000Z';
|
||||
|
||||
const ai = () => new AiJobService(asService, makeFakePorts(), makeFakeInference(), new AiBudgetGuard(asService), actors);
|
||||
const outbound = () => new OutboundService(asService, new AdapterRegistry());
|
||||
const outbound = () => new OutboundService(asService, new CapabilityBroker(makeFakePorts(), new CapabilityProviderRegistry()));
|
||||
const cal = (ports?: FakePorts) => new CalendarService(asService, ports ?? makeFakePorts(), actors, ai(), outbound());
|
||||
|
||||
async function makeInteraction(text: string): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user