feat(auto-reply): greet on bare /new
This commit is contained in:
@@ -145,7 +145,7 @@ Example:
|
||||
|
||||
- Session files: `~/.clawdis/sessions/{{SessionId}}.jsonl`
|
||||
- Session metadata (token usage, last route, etc): `~/.clawdis/sessions/sessions.json` (legacy: `~/.clawdis/sessions.json`)
|
||||
- `/new` starts a fresh session for that chat (configurable via `resetTriggers`)
|
||||
- `/new` starts a fresh session for that chat (configurable via `resetTriggers`). If sent alone, the agent replies with a short hello to confirm the reset.
|
||||
|
||||
## Heartbeats (proactive mode)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ All session state is **owned by the gateway** (the “master” Clawdis). UI cli
|
||||
|
||||
## Lifecyle
|
||||
- Idle expiry: `inbound.session.idleMinutes` (default 60). After the timeout a new `sessionId` is minted on the next message.
|
||||
- Reset triggers: exact `/new` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through.
|
||||
- Reset triggers: exact `/new` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through. If `/new` is sent alone, Clawdis runs a short “hello” greeting turn to confirm the reset.
|
||||
- Manual reset: delete specific keys from the store or remove the JSONL transcript; the next message recreates them.
|
||||
|
||||
## Configuration (optional rename example)
|
||||
|
||||
@@ -98,8 +98,16 @@ describe("trigger handling", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("acknowledges a bare /new without treating it as empty", async () => {
|
||||
it("runs a greeting prompt for a bare /new", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||
payloads: [{ text: "hello" }],
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "/new",
|
||||
@@ -119,8 +127,11 @@ describe("trigger handling", () => {
|
||||
},
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toMatch(/fresh session/i);
|
||||
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||
expect(text).toBe("hello");
|
||||
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
|
||||
const prompt =
|
||||
vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0]?.prompt ?? "";
|
||||
expect(prompt).toContain("A new session was started via /new");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ const ABORT_TRIGGERS = new Set(["stop", "esc", "abort", "wait", "exit"]);
|
||||
const ABORT_MEMORY = new Map<string, boolean>();
|
||||
const SYSTEM_MARK = "⚙️";
|
||||
|
||||
const BARE_SESSION_RESET_PROMPT =
|
||||
"A new session was started via /new. Say hi briefly and ask what the user wants to do next.";
|
||||
|
||||
export function extractThinkDirective(body?: string): {
|
||||
cleaned: string;
|
||||
thinkLevel?: ThinkLevel;
|
||||
@@ -580,20 +583,18 @@ export async function getReplyFromConfig(
|
||||
})()
|
||||
: "";
|
||||
const baseBody = sessionCtx.BodyStripped ?? sessionCtx.Body ?? "";
|
||||
const baseBodyTrimmed = baseBody.trim();
|
||||
const rawBodyTrimmed = (ctx.Body ?? "").trim();
|
||||
const baseBodyTrimmedRaw = baseBody.trim();
|
||||
const isBareSessionReset =
|
||||
isNewSession && baseBodyTrimmed.length === 0 && rawBodyTrimmed.length > 0;
|
||||
isNewSession &&
|
||||
baseBodyTrimmedRaw.length === 0 &&
|
||||
rawBodyTrimmed.length > 0;
|
||||
const baseBodyFinal = isBareSessionReset ? BARE_SESSION_RESET_PROMPT : baseBody;
|
||||
const baseBodyTrimmed = baseBodyFinal.trim();
|
||||
// Bail early if the cleaned body is empty to avoid sending blank prompts to the agent.
|
||||
// This can happen if an inbound platform delivers an empty text message or we strip everything out.
|
||||
if (!baseBodyTrimmed) {
|
||||
await onReplyStart();
|
||||
if (isBareSessionReset) {
|
||||
cleanupTyping();
|
||||
return {
|
||||
text: "Started a fresh session. Send a new message to continue.",
|
||||
};
|
||||
}
|
||||
logVerbose("Inbound body empty after normalization; skipping agent run");
|
||||
cleanupTyping();
|
||||
return {
|
||||
@@ -603,7 +604,7 @@ export async function getReplyFromConfig(
|
||||
const abortedHint = abortedLastRun
|
||||
? "Note: The previous agent run was aborted by the user. Resume carefully or ask for clarification."
|
||||
: "";
|
||||
let prefixedBodyBase = baseBody;
|
||||
let prefixedBodyBase = baseBodyFinal;
|
||||
if (groupIntro) {
|
||||
prefixedBodyBase = `${groupIntro}\n\n${prefixedBodyBase}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user