




















@@ -19,8 +19,10 @@ import {
1919buildInboundMetaSystemPrompt,
2020buildInboundUserContextPrefix,
2121} from "../../../src/auto-reply/reply/inbound-meta.js";
22+import { buildReplyPromptEnvelope } from "../../../src/auto-reply/reply/prompt-prelude.js";
2223import type { TemplateContext } from "../../../src/auto-reply/templating.js";
2324import { SILENT_REPLY_TOKEN } from "../../../src/auto-reply/tokens.js";
25+import { buildCurrentTurnPrompt } from "../../../src/agents/pi-embedded-runner/run/runtime-context-prompt.js";
2426import type { OpenClawConfig } from "../../../src/config/config.js";
2527import { makeTempWorkspace, writeWorkspaceFile } from "../../../src/test-helpers/workspace.js";
2628@@ -106,6 +108,23 @@ function buildAutoReplyBody(params: { ctx: TemplateContext; body: string; eventL
106108.join("\n\n");
107109}
108110111+function buildAutoReplyModelPrompt(params: { ctx: TemplateContext; body: string }): string {
112+const inboundUserContext = buildInboundUserContextPrefix(params.ctx);
113+const envelope = buildReplyPromptEnvelope({
114+ctx: params.ctx,
115+sessionCtx: params.ctx,
116+baseBody: params.body,
117+hasUserBody: true,
118+ inboundUserContext,
119+isBareSessionReset: false,
120+startupAction: "new",
121+});
122+return buildCurrentTurnPrompt({
123+context: envelope.currentTurnContext,
124+prompt: envelope.queuedBody,
125+});
126+}
127+109128async function readContextFiles(workspaceDir: string, fileNames: string[]) {
110129return Promise.all(
111130fileNames.map(async (fileName) => ({
@@ -422,6 +441,60 @@ function createGroupScenario(workspaceDir: string): PromptScenario {
422441};
423442}
424443444+function createDiscordBoundaryScenario(workspaceDir: string): PromptScenario {
445+const body = "Please summarize the deploy log.";
446+const baseCtx: TemplateContext = {
447+Provider: "discord",
448+Surface: "discord",
449+OriginatingChannel: "discord",
450+OriginatingTo: "channel:987654321",
451+AccountId: "A1",
452+ChatType: "channel",
453+GroupSubject: "#ops-bridge",
454+GroupChannel: "#ops-bridge",
455+GroupSpace: "guild-123",
456+SenderId: "U3",
457+SenderName: "Cael",
458+MessageSid: "1503084621145964846",
459+Body: body,
460+BodyStripped: body,
461+UntrustedStructuredContext: [
462+{
463+label: "Discord channel metadata",
464+source: "discord",
465+type: "channel_metadata",
466+payload: {
467+topic: "Deploy coordination",
468+},
469+},
470+],
471+};
472+return {
473+scenario: "auto-reply-discord-boundary",
474+focus: "Discord inbound body remains one user turn while supplemental context is structured metadata",
475+expectedStableSystemAfterTurnIds: [],
476+turns: [
477+{
478+id: "t1",
479+label: "Discord turn with channel metadata",
480+systemPrompt: buildAutoReplySystemPrompt({
481+ workspaceDir,
482+sessionCtx: baseCtx,
483+includeGroupChatContext: true,
484+}),
485+bodyPrompt: buildAutoReplyModelPrompt({
486+ctx: baseCtx,
487+ body,
488+}),
489+notes: [
490+"Inbound body should appear once in the model-bound prompt",
491+"Channel metadata should not use raw EXTERNAL_UNTRUSTED_CONTENT wrappers",
492+],
493+},
494+],
495+};
496+}
497+425498async function createToolRichScenario(workspaceDir: string): Promise<PromptScenario> {
426499const skillsPrompt = [
427500"<available_skills>",
@@ -701,6 +774,7 @@ export async function createPromptCompositionScenarios(): Promise<{
701774const scenarios = [
702775createDirectScenario(workspaceDir),
703776createGroupScenario(workspaceDir),
777+createDiscordBoundaryScenario(workspaceDir),
704778await createToolRichScenario(workspaceDir),
705779await createBootstrapWarningScenario(warningWorkspaceDir),
706780await createMaintenanceScenario(workspaceDir),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。