Auto-reply: add verbose session hint

This commit is contained in:
Peter Steinberger
2025-12-03 09:07:17 +00:00
parent 086dd284d6
commit ae0d35c727
2 changed files with 91 additions and 45 deletions

View File

@@ -489,11 +489,11 @@ export async function getReplyFromConfig(
const isHeartbeat = opts?.isHeartbeat === true; const isHeartbeat = opts?.isHeartbeat === true;
if (reply && reply.mode === "command") { if (reply && reply.mode === "command") {
const heartbeatCommand = isHeartbeat const heartbeatCommand = isHeartbeat
? (reply as { heartbeatCommand?: string[] }).heartbeatCommand ? (reply as { heartbeatCommand?: string[] }).heartbeatCommand
: undefined; : undefined;
const commandArgs = heartbeatCommand?.length const commandArgs = heartbeatCommand?.length
? heartbeatCommand ? heartbeatCommand
: reply.command; : reply.command;
@@ -502,38 +502,34 @@ export async function getReplyFromConfig(
return undefined; return undefined;
} }
await onReplyStart(); await onReplyStart();
const commandReply = { const commandReply = {
...reply, ...reply,
command: commandArgs, command: commandArgs,
mode: "command" as const, mode: "command" as const,
}; };
try { try {
const runResult = await runCommandReply({ const runResult = await runCommandReply({
reply: commandReply, reply: commandReply,
templatingCtx, templatingCtx,
sendSystemOnce, sendSystemOnce,
isNewSession, isNewSession,
isFirstTurnInSession, isFirstTurnInSession,
systemSent, systemSent,
timeoutMs, timeoutMs,
timeoutSeconds, timeoutSeconds,
commandRunner, commandRunner,
thinkLevel: resolvedThinkLevel, thinkLevel: resolvedThinkLevel,
verboseLevel: resolvedVerboseLevel, verboseLevel: resolvedVerboseLevel,
}); });
const payloadArray = runResult.payloads ?? []; const payloadArray = runResult.payloads ?? [];
const meta = runResult.meta; const meta = runResult.meta;
const normalizedPayloads = let finalPayloads = payloadArray;
payloadArray.length === 1 ? payloadArray[0] : payloadArray; if (!finalPayloads || finalPayloads.length === 0) {
if ( return undefined;
!normalizedPayloads || }
(Array.isArray(normalizedPayloads) && normalizedPayloads.length === 0) if (sessionCfg && sessionStore && sessionKey) {
) { const returnedSessionId = meta.agentMeta?.sessionId;
return undefined;
}
if (sessionCfg && sessionStore && sessionKey) {
const returnedSessionId = meta.agentMeta?.sessionId;
if (returnedSessionId && returnedSessionId !== sessionId) { if (returnedSessionId && returnedSessionId !== sessionId) {
const entry = sessionEntry ?? const entry = sessionEntry ??
sessionStore[sessionKey] ?? { sessionStore[sessionKey] ?? {
@@ -557,14 +553,28 @@ export async function getReplyFromConfig(
} }
} }
} }
if (meta.agentMeta && isVerbose()) { if (meta.agentMeta && isVerbose()) {
logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`); logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`);
} }
return normalizedPayloads; // If verbose is enabled and this is a new session, prepend a session hint.
} finally { const sessionIdHint =
cleanupTyping(); resolvedVerboseLevel === "on" && isNewSession
} ? sessionId ??
} meta.agentMeta?.sessionId ??
templatingCtx.SessionId ??
"unknown"
: undefined;
if (sessionIdHint) {
finalPayloads = [
{ text: `🧭 New session: ${sessionIdHint}` },
...payloadArray,
];
}
return finalPayloads.length === 1 ? finalPayloads[0] : finalPayloads;
} finally {
cleanupTyping();
}
}
cleanupTyping(); cleanupTyping();
return undefined; return undefined;

View File

@@ -750,6 +750,42 @@ describe("config and templating", () => {
); );
}); });
it("prepends session hint when new session and verbose on", async () => {
const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockResolvedValue({
stdout: "ok",
stderr: "",
code: 0,
signal: null,
killed: false,
});
vi.spyOn(crypto, "randomUUID").mockReturnValue("sess-uuid");
const storeDir = await fs.promises.mkdtemp(
path.join(os.tmpdir(), "warelay-session-"),
);
const storePath = path.join(storeDir, "sessions.json");
const cfg = {
inbound: {
reply: {
mode: "command" as const,
command: ["echo", "{{Body}}"],
agent: { kind: "claude" },
session: { store: storePath },
},
},
};
const res = await index.getReplyFromConfig(
{ Body: "/new /v on hi", From: "+1", To: "+2" },
undefined,
cfg,
runSpy,
);
const payloads = Array.isArray(res) ? res : res ? [res] : [];
expect(payloads[0]?.text).toBe("🧭 New session: sess-uuid");
expect(payloads[1]?.text).toBe("ok");
});
it("treats directive-only even when bracket prefixes are present", async () => { it("treats directive-only even when bracket prefixes are present", async () => {
const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockResolvedValue({ const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockResolvedValue({
stdout: "ok", stdout: "ok",