fix: inject group activation in system prompt
This commit is contained in:
@@ -267,6 +267,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
data: Record<string, unknown>;
|
||||
}) => void;
|
||||
enqueue?: typeof enqueueCommand;
|
||||
extraSystemPrompt?: string;
|
||||
}): Promise<EmbeddedPiRunResult> {
|
||||
const enqueue = params.enqueue ?? enqueueCommand;
|
||||
return enqueue(async () => {
|
||||
@@ -326,6 +327,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
appendPrompt: buildAgentSystemPromptAppend({
|
||||
workspaceDir: resolvedWorkspace,
|
||||
defaultThinkLevel: params.thinkLevel,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
}),
|
||||
contextFiles,
|
||||
skills: promptSkills,
|
||||
|
||||
@@ -3,13 +3,16 @@ import type { ThinkLevel } from "../auto-reply/thinking.js";
|
||||
export function buildAgentSystemPromptAppend(params: {
|
||||
workspaceDir: string;
|
||||
defaultThinkLevel?: ThinkLevel;
|
||||
extraSystemPrompt?: string;
|
||||
}) {
|
||||
const thinkHint =
|
||||
params.defaultThinkLevel && params.defaultThinkLevel !== "off"
|
||||
? `Default thinking level: ${params.defaultThinkLevel}.`
|
||||
: "Default thinking level: off.";
|
||||
|
||||
return [
|
||||
const extraSystemPrompt = params.extraSystemPrompt?.trim();
|
||||
|
||||
const lines = [
|
||||
"You are Clawd, a personal assistant running inside Clawdis.",
|
||||
"",
|
||||
"## Tooling",
|
||||
@@ -35,6 +38,13 @@ export function buildAgentSystemPromptAppend(params: {
|
||||
"Never send streaming/partial replies to external messaging surfaces; only final replies should be delivered there.",
|
||||
"Clawdis handles message transport automatically; respond normally and your reply will be delivered to the current chat.",
|
||||
"",
|
||||
];
|
||||
|
||||
if (extraSystemPrompt) {
|
||||
lines.push("## Group Chat Context", extraSystemPrompt, "");
|
||||
}
|
||||
|
||||
lines.push(
|
||||
"## Heartbeats",
|
||||
'If you receive a heartbeat poll (a user message containing just "HEARTBEAT"), and there is nothing that needs attention, reply exactly:',
|
||||
"HEARTBEAT_OK",
|
||||
@@ -42,7 +52,9 @@ export function buildAgentSystemPromptAppend(params: {
|
||||
"",
|
||||
"## Runtime",
|
||||
thinkHint,
|
||||
]
|
||||
);
|
||||
|
||||
return lines
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
@@ -142,6 +142,49 @@ describe("trigger handling", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("injects group activation context into the system prompt", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "hello group",
|
||||
From: "123@g.us",
|
||||
To: "+2000",
|
||||
ChatType: "group",
|
||||
SenderE164: "+2000",
|
||||
GroupSubject: "Test Group",
|
||||
GroupMembers: "Alice (+1), Bob (+2)",
|
||||
},
|
||||
{},
|
||||
{
|
||||
inbound: {
|
||||
allowFrom: ["*"],
|
||||
workspace: join(home, "clawd"),
|
||||
agent: { provider: "anthropic", model: "claude-opus-4-5" },
|
||||
session: { store: join(home, "sessions.json") },
|
||||
groupChat: { requireMention: false },
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toBe("ok");
|
||||
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
|
||||
const extra =
|
||||
vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0]?.extraSystemPrompt ??
|
||||
"";
|
||||
expect(extra).toContain("Test Group");
|
||||
expect(extra).toContain("Activation: always-on");
|
||||
});
|
||||
});
|
||||
|
||||
it("runs a greeting prompt for a bare /new", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||
|
||||
@@ -563,6 +563,7 @@ export async function getReplyFromConfig(
|
||||
}
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
sessionEntry.groupActivation = activationCommand.mode;
|
||||
sessionEntry.groupActivationNeedsSystemIntro = true;
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
@@ -648,8 +649,11 @@ export async function getReplyFromConfig(
|
||||
await startTypingLoop();
|
||||
|
||||
const isFirstTurnInSession = isNewSession || !systemSent;
|
||||
const shouldInjectGroupIntro =
|
||||
sessionCtx.ChatType === "group" &&
|
||||
(isFirstTurnInSession || sessionEntry?.groupActivationNeedsSystemIntro);
|
||||
const groupIntro =
|
||||
isFirstTurnInSession && sessionCtx.ChatType === "group"
|
||||
shouldInjectGroupIntro
|
||||
? (() => {
|
||||
const activation =
|
||||
normalizeGroupActivation(sessionEntry?.groupActivation) ??
|
||||
@@ -713,9 +717,6 @@ export async function getReplyFromConfig(
|
||||
? "Note: The previous agent run was aborted by the user. Resume carefully or ask for clarification."
|
||||
: "";
|
||||
let prefixedBodyBase = baseBodyFinal;
|
||||
if (groupIntro) {
|
||||
prefixedBodyBase = `${groupIntro}\n\n${prefixedBodyBase}`;
|
||||
}
|
||||
if (abortedHint) {
|
||||
prefixedBodyBase = `${abortedHint}\n\n${prefixedBodyBase}`;
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
@@ -875,6 +876,7 @@ export async function getReplyFromConfig(
|
||||
config: cfg,
|
||||
skillsSnapshot,
|
||||
prompt: commandBody,
|
||||
extraSystemPrompt: groupIntro || undefined,
|
||||
provider,
|
||||
model,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
@@ -898,6 +900,19 @@ export async function getReplyFromConfig(
|
||||
: undefined,
|
||||
});
|
||||
|
||||
if (
|
||||
shouldInjectGroupIntro &&
|
||||
sessionEntry &&
|
||||
sessionStore &&
|
||||
sessionKey &&
|
||||
sessionEntry.groupActivationNeedsSystemIntro
|
||||
) {
|
||||
sessionEntry.groupActivationNeedsSystemIntro = false;
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
|
||||
const payloadArray = runResult.payloads ?? [];
|
||||
if (payloadArray.length === 0) return undefined;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export type SessionEntry = {
|
||||
thinkingLevel?: string;
|
||||
verboseLevel?: string;
|
||||
groupActivation?: "mention" | "always";
|
||||
groupActivationNeedsSystemIntro?: boolean;
|
||||
inputTokens?: number;
|
||||
outputTokens?: number;
|
||||
totalTokens?: number;
|
||||
|
||||
Reference in New Issue
Block a user