fix: rename createInboundCall → createWebhookCall, preserve event direction

Address Greptile review: externally-initiated outbound-api calls were
stored with hardcoded direction: "inbound". Now createWebhookCall accepts
a direction parameter so the CallRecord accurately reflects the event's
actual direction. Also skip inboundGreeting for outbound calls and add a
test asserting inbound direction is preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
scoootscooob
2026-03-01 18:38:08 -08:00
committed by Peter Steinberger
parent a1b4a0066b
commit 24dcd68f42
2 changed files with 43 additions and 6 deletions

View File

@@ -235,7 +235,7 @@ describe("processEvent (functional)", () => {
expect(ctx.activeCalls.size).toBe(0);
});
it("auto-registers externally-initiated outbound-api calls", () => {
it("auto-registers externally-initiated outbound-api calls with correct direction", () => {
const ctx = createContext();
const event: NormalizedEvent = {
id: "evt-external-1",
@@ -255,6 +255,7 @@ describe("processEvent (functional)", () => {
expect(ctx.providerCallIdMap.get("CA-external-123")).toBeDefined();
const call = [...ctx.activeCalls.values()][0];
expect(call?.providerCallId).toBe("CA-external-123");
expect(call?.direction).toBe("outbound");
expect(call?.from).toBe("+15550000000");
expect(call?.to).toBe("+15559876543");
});
@@ -277,6 +278,35 @@ describe("processEvent (functional)", () => {
// External outbound calls bypass inbound policy — they should be accepted
expect(ctx.activeCalls.size).toBe(1);
expect(hangupCalls).toHaveLength(0);
const call = [...ctx.activeCalls.values()][0];
expect(call?.direction).toBe("outbound");
});
it("preserves inbound direction for auto-registered inbound calls", () => {
const ctx = createContext({
config: VoiceCallConfigSchema.parse({
enabled: true,
provider: "plivo",
fromNumber: "+15550000000",
inboundPolicy: "open",
}),
});
const event: NormalizedEvent = {
id: "evt-inbound-dir",
type: "call.initiated",
callId: "CA-inbound-789",
providerCallId: "CA-inbound-789",
timestamp: Date.now(),
direction: "inbound",
from: "+15554444444",
to: "+15550000000",
};
processEvent(ctx, event);
expect(ctx.activeCalls.size).toBe(1);
const call = [...ctx.activeCalls.values()][0];
expect(call?.direction).toBe("inbound");
});
it("deduplicates by dedupeKey even when event IDs differ", () => {

View File

@@ -59,9 +59,10 @@ function shouldAcceptInbound(config: EventContext["config"], from: string | unde
}
}
function createInboundCall(params: {
function createWebhookCall(params: {
ctx: EventContext;
providerCallId: string;
direction: "inbound" | "outbound";
from: string;
to: string;
}): CallRecord {
@@ -71,7 +72,7 @@ function createInboundCall(params: {
callId,
providerCallId: params.providerCallId,
provider: params.ctx.provider?.name || "twilio",
direction: "inbound",
direction: params.direction,
state: "ringing",
from: params.from,
to: params.to,
@@ -79,7 +80,10 @@ function createInboundCall(params: {
transcript: [],
processedEventIds: [],
metadata: {
initialMessage: params.ctx.config.inboundGreeting || "Hello! How can I help you today?",
initialMessage:
params.direction === "inbound"
? params.ctx.config.inboundGreeting || "Hello! How can I help you today?"
: undefined,
},
};
@@ -87,7 +91,9 @@ function createInboundCall(params: {
params.ctx.providerCallIdMap.set(params.providerCallId, callId);
persistCallRecord(params.ctx.storePath, callRecord);
console.log(`[voice-call] Created inbound call record: ${callId} from ${params.from}`);
console.log(
`[voice-call] Created ${params.direction} call record: ${callId} from ${params.from}`,
);
return callRecord;
}
@@ -142,9 +148,10 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void {
return;
}
call = createInboundCall({
call = createWebhookCall({
ctx,
providerCallId: event.providerCallId,
direction: event.direction === "outbound" ? "outbound" : "inbound",
from: event.from || "unknown",
to: event.to || ctx.config.fromNumber || "unknown",
});