(req: Request)
| 821 | } |
| 822 | |
| 823 | async function handleSendMessage(req: Request): Promise<Response> { |
| 824 | let body: SendRequest; |
| 825 | try { |
| 826 | body = (await req.json()) as SendRequest; |
| 827 | } catch { |
| 828 | return errorJson("invalid_json", 400); |
| 829 | } |
| 830 | if ( |
| 831 | !body || |
| 832 | typeof body !== "object" || |
| 833 | typeof body.sender_session !== "string" || |
| 834 | typeof body.prompt !== "string" |
| 835 | ) { |
| 836 | return errorJson("invalid_request", 400); |
| 837 | } |
| 838 | const projectName = body.project ?? "default"; |
| 839 | const p = state.projects.get(projectName); |
| 840 | if (!p) return errorJson("agent_not_found", 404); |
| 841 | |
| 842 | const sender = p.agents.get(body.sender_session); |
| 843 | if (!sender) return errorJson("sender_not_registered", 404); |
| 844 | |
| 845 | const hops = typeof body.hops === "number" ? body.hops : 0; |
| 846 | if (hops >= MAX_HOPS) { |
| 847 | logRejected("hop_limit", `${sender.name} hops=${hops} max=${MAX_HOPS}`); |
| 848 | return errorJson("hop_limit_exceeded", 409, { hops, max_hops: MAX_HOPS }); |
| 849 | } |
| 850 | |
| 851 | // Resolve target. |
| 852 | let target: RegistryEntry | undefined; |
| 853 | if (body.target_session && typeof body.target_session === "string") { |
| 854 | target = p.agents.get(body.target_session); |
| 855 | if (!target) { |
| 856 | logRejected("target_not_found", `${sender.name} → ${body.target_session.slice(-6)}`); |
| 857 | return errorJson("target_not_found", 404); |
| 858 | } |
| 859 | } else { |
| 860 | const desired = (body.target ?? "").trim(); |
| 861 | if (!desired) return errorJson("missing_target", 400); |
| 862 | // Direct session_id match first. |
| 863 | const directSid = p.agents.get(desired); |
| 864 | if (directSid) { |
| 865 | target = directSid; |
| 866 | } else { |
| 867 | const bag = p.nameIndex.get(desired); |
| 868 | if (!bag || bag.size === 0) { |
| 869 | logRejected("target_not_found", `${sender.name} → "${desired}"`); |
| 870 | return errorJson("target_not_found", 404, { target: desired }); |
| 871 | } |
| 872 | if (bag.size > 1) { |
| 873 | logRejected("ambiguous", `${sender.name} → "${desired}" matches ${bag.size}`); |
| 874 | return errorJson("ambiguous_target", 409, { |
| 875 | target: desired, |
| 876 | candidates: [...bag], |
| 877 | }); |
| 878 | } |
| 879 | const onlySid = [...bag][0]; |
| 880 | target = p.agents.get(onlySid); |
no test coverage detected