fix: delay typing until reply payload
parent
cba12a1abd
commit
863d26558a
|
|
@ -17,6 +17,7 @@
|
||||||
- Tailscale Funnel now requires password auth (no token-only public exposure).
|
- Tailscale Funnel now requires password auth (no token-only public exposure).
|
||||||
- Group `/new` resets now work with @mentions so activation guidance appears on fresh sessions.
|
- Group `/new` resets now work with @mentions so activation guidance appears on fresh sessions.
|
||||||
- Group chat activation context is now injected into the system prompt at session start (and after activation changes), including /new greetings.
|
- Group chat activation context is now injected into the system prompt at session start (and after activation changes), including /new greetings.
|
||||||
|
- Typing indicators now start only once a reply payload is produced (no "thinking" typing for silent runs).
|
||||||
- Canvas defaults/A2UI auto-nav aligned; debug status overlay centered; redundant await removed in `CanvasManager`.
|
- Canvas defaults/A2UI auto-nav aligned; debug status overlay centered; redundant await removed in `CanvasManager`.
|
||||||
- Gateway launchd loop fixed by removing redundant `kickstart -k`.
|
- Gateway launchd loop fixed by removing redundant `kickstart -k`.
|
||||||
- CLI now hints when Peekaboo is unauthorized.
|
- CLI now hints when Peekaboo is unauthorized.
|
||||||
|
|
|
||||||
|
|
@ -201,11 +201,17 @@ export async function getReplyFromConfig(
|
||||||
if (!opts?.onReplyStart) return;
|
if (!opts?.onReplyStart) return;
|
||||||
if (typingIntervalMs <= 0) return;
|
if (typingIntervalMs <= 0) return;
|
||||||
if (typingTimer) return;
|
if (typingTimer) return;
|
||||||
await triggerTyping();
|
await onReplyStart();
|
||||||
typingTimer = setInterval(() => {
|
typingTimer = setInterval(() => {
|
||||||
void triggerTyping();
|
void triggerTyping();
|
||||||
}, typingIntervalMs);
|
}, typingIntervalMs);
|
||||||
};
|
};
|
||||||
|
const startTypingOnText = async (text?: string) => {
|
||||||
|
const trimmed = text?.trim();
|
||||||
|
if (!trimmed) return;
|
||||||
|
if (trimmed === SILENT_REPLY_TOKEN) return;
|
||||||
|
await startTypingLoop();
|
||||||
|
};
|
||||||
let transcribedText: string | undefined;
|
let transcribedText: string | undefined;
|
||||||
|
|
||||||
// Optional audio transcription before templating/session handling.
|
// Optional audio transcription before templating/session handling.
|
||||||
|
|
@ -646,8 +652,6 @@ export async function getReplyFromConfig(
|
||||||
return { text: "⚙️ Agent was aborted." };
|
return { text: "⚙️ Agent was aborted." };
|
||||||
}
|
}
|
||||||
|
|
||||||
await startTypingLoop();
|
|
||||||
|
|
||||||
const isFirstTurnInSession = isNewSession || !systemSent;
|
const isFirstTurnInSession = isNewSession || !systemSent;
|
||||||
const shouldInjectGroupIntro =
|
const shouldInjectGroupIntro =
|
||||||
sessionCtx.ChatType === "group" &&
|
sessionCtx.ChatType === "group" &&
|
||||||
|
|
@ -865,8 +869,6 @@ export async function getReplyFromConfig(
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
await onReplyStart();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const runId = crypto.randomUUID();
|
const runId = crypto.randomUUID();
|
||||||
const runResult = await runEmbeddedPiAgent({
|
const runResult = await runEmbeddedPiAgent({
|
||||||
|
|
@ -884,19 +886,23 @@ export async function getReplyFromConfig(
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
runId,
|
runId,
|
||||||
onPartialReply: opts?.onPartialReply
|
onPartialReply: opts?.onPartialReply
|
||||||
? (payload) =>
|
? async (payload) => {
|
||||||
opts.onPartialReply?.({
|
await startTypingOnText(payload.text);
|
||||||
|
await opts.onPartialReply?.({
|
||||||
text: payload.text,
|
text: payload.text,
|
||||||
mediaUrls: payload.mediaUrls,
|
mediaUrls: payload.mediaUrls,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
shouldEmitToolResult,
|
shouldEmitToolResult,
|
||||||
onToolResult: opts?.onToolResult
|
onToolResult: opts?.onToolResult
|
||||||
? (payload) =>
|
? async (payload) => {
|
||||||
opts.onToolResult?.({
|
await startTypingOnText(payload.text);
|
||||||
|
await opts.onToolResult?.({
|
||||||
text: payload.text,
|
text: payload.text,
|
||||||
mediaUrls: payload.mediaUrls,
|
mediaUrls: payload.mediaUrls,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -915,6 +921,16 @@ export async function getReplyFromConfig(
|
||||||
|
|
||||||
const payloadArray = runResult.payloads ?? [];
|
const payloadArray = runResult.payloads ?? [];
|
||||||
if (payloadArray.length === 0) return undefined;
|
if (payloadArray.length === 0) return undefined;
|
||||||
|
const shouldSignalTyping = payloadArray.some((payload) => {
|
||||||
|
const trimmed = payload.text?.trim();
|
||||||
|
if (trimmed && trimmed !== SILENT_REPLY_TOKEN) return true;
|
||||||
|
if (payload.mediaUrl) return true;
|
||||||
|
if (payload.mediaUrls && payload.mediaUrls.length > 0) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (shouldSignalTyping) {
|
||||||
|
await onReplyStart();
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionStore && sessionKey) {
|
if (sessionStore && sessionKey) {
|
||||||
const usage = runResult.meta.agentMeta?.usage;
|
const usage = runResult.meta.agentMeta?.usage;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue