diff --git a/docs/CONFIG.example.json b/docs/CONFIG.example.json index 0c887fb..d14f120 100644 --- a/docs/CONFIG.example.json +++ b/docs/CONFIG.example.json @@ -40,8 +40,8 @@ "reasoning": false, "input": ["text"], "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }, - "contextWindow": 4096, - "maxTokens": 64 + "contextWindow": 200000, + "maxTokens": 8192 } ] } diff --git a/no-reply-api/package-lock.json b/no-reply-api/package-lock.json new file mode 100644 index 0000000..3e29cb5 --- /dev/null +++ b/no-reply-api/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "whispergate-no-reply-api", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "whispergate-no-reply-api", + "version": "0.1.0" + } + } +} diff --git a/plugin/index.ts b/plugin/index.ts index c77f9a5..7f8ce75 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -356,32 +356,47 @@ export default { } }); - api.on("before_model_resolve", async (_event, ctx) => { + api.on("before_model_resolve", async (event, ctx) => { const key = ctx.sessionKey; if (!key) return; const live = getLivePluginConfig(api, baseConfig as WhisperGateConfig) as WhisperGateConfig & DebugConfig; ensurePolicyStateLoaded(api, live); + // In before_model_resolve, ctx only has: agentId, sessionKey, sessionId, workspaceDir, messageProvider. + // senderId/channelId/input are NOT available. Use event.prompt (user message incl. untrusted metadata). + const channel = (ctx.messageProvider || "").toLowerCase(); + if (live.discordOnly !== false && channel !== "discord") return; + + const prompt = ((event as Record).prompt as string) || ""; + const conv = extractUntrustedConversationInfo(prompt) || {}; + const senderId = + (typeof conv.sender_id === "string" && conv.sender_id) || + (typeof conv.sender === "string" && conv.sender) || + undefined; + const channelId = + (typeof conv.channel_id === "string" && conv.channel_id) || + (typeof (conv as Record).conversation_label === "string" + ? undefined + : undefined); + let rec = sessionDecision.get(key); if (!rec || Date.now() - rec.createdAt > DECISION_TTL_MS) { if (rec) sessionDecision.delete(key); - const c = (ctx || {}) as Record; - const derived = deriveDecisionInputFromAgentCtx(c); const decision = evaluateDecision({ config: live, - channel: derived.channel, - channelId: derived.channelId, + channel, + channelId, channelPolicies: policyState.channelPolicies, - senderId: derived.senderId, - content: derived.content, + senderId, + content: prompt, }); rec = { decision, createdAt: Date.now() }; sessionDecision.set(key, rec); pruneDecisionMap(); - if (shouldDebugLog(live, derived.channelId ?? ctx.channelId)) { + if (shouldDebugLog(live, channelId)) { api.logger.info( - `whispergate: debug before_model_resolve recompute session=${key} decision=${decision.reason} ` + + `whispergate: debug before_model_resolve recompute session=${key} senderId=${senderId} decision=${decision.reason} ` + `shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, ); } @@ -399,7 +414,7 @@ export default { }; }); - api.on("before_prompt_build", async (_event, ctx) => { + api.on("before_prompt_build", async (event, ctx) => { const key = ctx.sessionKey; if (!key) return; @@ -409,20 +424,31 @@ export default { let rec = sessionDecision.get(key); if (!rec || Date.now() - rec.createdAt > DECISION_TTL_MS) { if (rec) sessionDecision.delete(key); - const c = (ctx || {}) as Record; - const derived = deriveDecisionInputFromAgentCtx(c); + + // before_prompt_build has event.prompt and event.messages available. + const channel = (ctx.messageProvider || "").toLowerCase(); + const prompt = ((event as Record).prompt as string) || ""; + const conv = extractUntrustedConversationInfo(prompt) || {}; + const senderId = + (typeof conv.sender_id === "string" && conv.sender_id) || + (typeof conv.sender === "string" && conv.sender) || + undefined; + const channelId = + (typeof conv.channel_id === "string" && conv.channel_id) || + undefined; + const decision = evaluateDecision({ config: live, - channel: derived.channel, - channelId: derived.channelId, + channel, + channelId, channelPolicies: policyState.channelPolicies, - senderId: derived.senderId, - content: derived.content, + senderId, + content: prompt, }); rec = { decision, createdAt: Date.now() }; - if (shouldDebugLog(live, derived.channelId ?? ctx.channelId)) { + if (shouldDebugLog(live, channelId)) { api.logger.info( - `whispergate: debug before_prompt_build recompute session=${key} decision=${decision.reason} ` + + `whispergate: debug before_prompt_build recompute session=${key} senderId=${senderId} decision=${decision.reason} ` + `shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, ); } @@ -430,7 +456,7 @@ export default { sessionDecision.delete(key); if (!rec.decision.shouldInjectEndMarkerPrompt) { - if (shouldDebugLog(live, ctx.channelId)) { + if (shouldDebugLog(live, undefined)) { api.logger.info( `whispergate: debug before_prompt_build session=${key} inject=false reason=${rec.decision.reason}`, ); diff --git a/scripts/install-whispergate-openclaw.mjs b/scripts/install-whispergate-openclaw.mjs index 7cc3e7b..73d7ad9 100755 --- a/scripts/install-whispergate-openclaw.mjs +++ b/scripts/install-whispergate-openclaw.mjs @@ -45,18 +45,6 @@ function runOpenclaw(args, { allowFail = false } = {}) { } } -function validateNoReplyModelAvailable() { - const modelRef = `${NO_REPLY_PROVIDER_ID}/${NO_REPLY_MODEL_ID}`; - const list = runOpenclaw(["models", "list"], { allowFail: true }) || ""; - if (!list.includes(modelRef)) { - throw new Error(`post-install validation failed: model not listed: ${modelRef}`); - } - - const status = runOpenclaw(["models", "status", "--json"], { allowFail: true }) || ""; - if (!status.includes(NO_REPLY_PROVIDER_ID)) { - throw new Error(`post-install validation failed: provider not visible in models status: ${NO_REPLY_PROVIDER_ID}`); - } -} function getJson(pathKey) { const out = runOpenclaw(["config", "get", pathKey, "--json"], { allowFail: true }); @@ -174,17 +162,15 @@ if (mode === "install") { }; setJson(PATH_PROVIDERS, providers); - runOpenclaw(["gateway", "restart"]); - validateNoReplyModelAvailable(); - const after = { [PATH_PLUGINS_LOAD]: getJson(PATH_PLUGINS_LOAD), [PATH_PLUGIN_ENTRY]: getJson(PATH_PLUGIN_ENTRY), [PATH_PROVIDERS]: getJson(PATH_PROVIDERS), }; writeRecord("install", before, after); - console.log("[whispergate] install ok"); + console.log("[whispergate] install ok (config written)"); console.log(`[whispergate] record: ${RECORD_PATH}`); + console.log("[whispergate] >>> restart gateway to apply: openclaw gateway restart"); } catch (e) { fs.copyFileSync(BACKUP_PATH, OPENCLAW_CONFIG_PATH); console.error(`[whispergate] install failed; rollback complete: ${String(e)}`);