Skip to content

Commit 96ab1da

Browse files
Fix WebChat targetless message sends
1 parent be94853 commit 96ab1da

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

src/infra/outbound/internal-source-reply.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,17 @@ export async function shouldUseInternalSourceReplySink(
8383
input: InternalSourceReplySinkInput,
8484
params: Record<string, unknown>,
8585
): Promise<boolean> {
86+
const sourceProvider = normalizeOptionalLowercaseString(
87+
input.toolContext?.currentChannelProvider,
88+
);
89+
const sourceCanUseImplicitSink =
90+
input.sourceReplyDeliveryMode === "message_tool_only" ||
91+
(input.sourceReplyDeliveryMode === "automatic" &&
92+
sourceProvider === INTERNAL_MESSAGE_CHANNEL &&
93+
!hasExternalSessionDeliveryRoute(input.sessionKey));
8694
const hasImplicitCurrentSourceRoute =
8795
input.action === "send" &&
88-
input.sourceReplyDeliveryMode === "message_tool_only" &&
96+
sourceCanUseImplicitSink &&
8997
hasCurrentSourceReplyContext(input) &&
9098
Boolean(input.sessionKey?.trim()) &&
9199
!hasExplicitRouteParam(params);

src/infra/outbound/message-action-runner.send-validation.test.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,52 @@ describe("runMessageAction send validation", () => {
143143
expect(JSON.stringify(result.toolResult?.content)).not.toContain("hello from codex");
144144
});
145145

146+
it("uses the current internal UI source as the automatic WebChat send sink", async () => {
147+
const result = await runMessageAction({
148+
cfg: emptyConfig,
149+
action: "send",
150+
params: {
151+
message: "hello from webchat",
152+
},
153+
toolContext: {
154+
currentChannelProvider: "webchat",
155+
},
156+
sessionKey: "agent:main",
157+
sourceReplyDeliveryMode: "automatic",
158+
});
159+
160+
expect(result).toMatchObject({
161+
kind: "send",
162+
channel: "webchat",
163+
to: "current-run",
164+
handledBy: "internal-source",
165+
payload: {
166+
status: "ok",
167+
deliveryStatus: "sent",
168+
sourceReplySink: "internal-ui",
169+
sourceReply: {
170+
text: "hello from webchat",
171+
},
172+
},
173+
});
174+
});
175+
176+
it("does not infer the automatic WebChat send sink when delivery mode is omitted", async () => {
177+
await expect(
178+
runMessageAction({
179+
cfg: emptyConfig,
180+
action: "send",
181+
params: {
182+
message: "hello from webchat",
183+
},
184+
toolContext: {
185+
currentChannelProvider: "webchat",
186+
},
187+
sessionKey: "agent:main",
188+
}),
189+
).rejects.toThrow(/requires a target/i);
190+
});
191+
146192
it.each(["agent:voice:agent:channel:room", "agent:main:telegram::group:room"])(
147193
"keeps malformed session route %s on the internal source sink",
148194
async (sessionKey) => {
@@ -237,18 +283,18 @@ describe("runMessageAction send validation", () => {
237283
expect(JSON.stringify(result.payload)).not.toContain("turn2view0");
238284
});
239285

240-
it("does not infer an internal UI sink outside message-tool-only source delivery", async () => {
286+
it("does not infer a non-webchat source sink outside message-tool-only source delivery", async () => {
241287
await expect(
242288
runMessageAction({
243289
cfg: emptyConfig,
244290
action: "send",
245291
params: {
246-
message: "hello from codex",
292+
message: "telegram reply",
247293
},
248294
toolContext: {
249-
currentChannelProvider: "webchat",
295+
currentChannelProvider: "telegram",
250296
},
251-
sessionKey: "agent:main",
297+
sessionKey: "agent:main:telegram:direct:123456789",
252298
sourceReplyDeliveryMode: "automatic",
253299
}),
254300
).rejects.toThrow(/requires a target/i);

0 commit comments

Comments
 (0)