Expose resolveSessionPeer on ctx.dshIm for ops schedulers (ops.23).

WhatsApp registers a peer resolver so cron_create can default IM delivery to the calling session's bot/target.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 21:48:00 +08:00
parent 6d2229acd2
commit 41f13bb90c
3 changed files with 42 additions and 2 deletions

View file

@ -17,7 +17,20 @@ export async function apply(ctx, config = {}) {
config.rpcOptions,
config.rpcAuthority,
);
let unregisterPeer = () => {};
try {
const dshIm = typeof ctx.get === 'function' ? ctx.get('dshIm') : undefined;
if (dshIm && typeof dshIm.registerPeerResolver === 'function'
&& typeof production.controller?.resolveChannelPeer === 'function') {
unregisterPeer = dshIm.registerPeerResolver((sessionId) => (
production.controller.resolveChannelPeer(sessionId)
));
}
} catch {
// dshIm optional during partial boots
}
ctx.effect(() => async () => {
unregisterPeer?.();
await unregisterDelivery?.();
await production.close();
}, 'dsh-im: close WhatsApp Web connections');

View file

@ -62,12 +62,39 @@ export function createImHostPlugin(internals = {}) {
inject,
async apply(ctx, config = {}) {
const deliveryService = makeDeliveryService();
const peerResolvers = [];
if (typeof ctx?.provide === 'function') {
ctx.provide('dshIm', Object.freeze({
send: (botId, targetId, text, options) => (
deliveryService.send(botId, targetId, text, options)
),
listTargets: async (botId) => (await deliveryService.listTargets(botId)).targets,
/**
* Resolve the IM channel peer for a Harness session (botId + conversationKey).
* Used by ops schedulers to default proactive delivery back to the caller.
*/
resolveSessionPeer: async (sessionId) => {
const id = typeof sessionId === 'string' ? sessionId.trim() : '';
if (!id) return null;
for (const resolve of peerResolvers) {
try {
const peer = await resolve(id);
if (peer && typeof peer.botId === 'string' && peer.botId.trim()) return peer;
} catch {
// try next channel
}
}
return null;
},
/** @param {(sessionId: string) => Promise<object|null>} resolve */
registerPeerResolver: (resolve) => {
if (typeof resolve !== 'function') return () => {};
peerResolvers.push(resolve);
return () => {
const index = peerResolvers.indexOf(resolve);
if (index >= 0) peerResolvers.splice(index, 1);
};
},
}));
}
const activate = async (readyCtx) => {