From 41f13bb90ca275fbfa816a2ea200acb6d9271673 Mon Sep 17 00:00:00 2001 From: oliver Date: Sat, 5 Sep 2026 21:48:00 +0800 Subject: [PATCH] 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 --- package.json | 4 +-- plugin-src/host/channels/whatsapp/index.mjs | 13 ++++++++++ plugin-src/host/index.mjs | 27 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b8265ba..28a8f66 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dsh-im-ops", - "version": "4.9.1-ops.22", - "description": "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 — access/session policies owned here.", + "version": "4.9.1-ops.23", + "description": "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 �access/session policies owned here.", "keywords": [ "deepseek-harness", "dsh", diff --git a/plugin-src/host/channels/whatsapp/index.mjs b/plugin-src/host/channels/whatsapp/index.mjs index ec42bbc..ecebe94 100644 --- a/plugin-src/host/channels/whatsapp/index.mjs +++ b/plugin-src/host/channels/whatsapp/index.mjs @@ -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'); diff --git a/plugin-src/host/index.mjs b/plugin-src/host/index.mjs index e6bb1de..e5e32d1 100644 --- a/plugin-src/host/index.mjs +++ b/plugin-src/host/index.mjs @@ -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} 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) => {