test: harden daemon lifecycle and e2e reliability across app/cli/server

This commit is contained in:
Mohamed Boudra
2026-02-25 10:21:46 +07:00
parent 094ce49d4f
commit a060ae1256
98 changed files with 5716 additions and 1771 deletions
@@ -3,18 +3,45 @@ import { __private__ } from "./use-agent-initialization";
describe("useAgentInitialization timeline request policy", () => {
it("uses canonical tail bootstrap when cursor is missing", () => {
expect(__private__.buildInitialTimelineRequest(undefined)).toEqual({
expect(
__private__.buildInitialTimelineRequest({
cursor: undefined,
hasLocalTail: false,
initialTimelineLimit: 200,
})
).toEqual({
direction: "tail",
limit: 200,
projection: "canonical",
});
});
it("uses canonical catch-up after the current cursor when present", () => {
it("uses canonical tail bootstrap when cursor exists but local tail is empty", () => {
expect(
__private__.buildInitialTimelineRequest({
epoch: "epoch-1",
endSeq: 42,
cursor: {
epoch: "epoch-1",
endSeq: 42,
},
hasLocalTail: false,
initialTimelineLimit: 200,
})
).toEqual({
direction: "tail",
limit: 200,
projection: "canonical",
});
});
it("uses canonical catch-up after the current cursor when local tail exists", () => {
expect(
__private__.buildInitialTimelineRequest({
cursor: {
epoch: "epoch-1",
endSeq: 42,
},
hasLocalTail: true,
initialTimelineLimit: 200,
})
).toEqual({
direction: "after",
@@ -23,4 +50,18 @@ describe("useAgentInitialization timeline request policy", () => {
projection: "canonical",
});
});
it("supports unbounded tail bootstrap policy", () => {
expect(
__private__.buildInitialTimelineRequest({
cursor: undefined,
hasLocalTail: false,
initialTimelineLimit: 0,
})
).toEqual({
direction: "tail",
limit: 0,
projection: "canonical",
});
});
});
@@ -1,4 +1,5 @@
import { useCallback } from "react";
import { Platform } from "react-native";
import { useSessionStore } from "@/stores/session-store";
import type {
DaemonClient,
@@ -13,20 +14,28 @@ import {
} from "@/utils/agent-initialization";
const INIT_TIMEOUT_MS = 5 * 60_000;
const DEFAULT_INITIAL_TIMELINE_LIMIT = 200;
const NATIVE_INITIAL_TIMELINE_LIMIT = 200;
const UNBOUNDED_TIMELINE_LIMIT = 0;
type TimelineCursorState = {
epoch: string;
endSeq: number;
};
type BuildInitialTimelineRequestInput = {
cursor: TimelineCursorState | undefined;
hasLocalTail: boolean;
initialTimelineLimit: number;
};
function buildInitialTimelineRequest(
cursor: TimelineCursorState | undefined
input: BuildInitialTimelineRequestInput
): FetchAgentTimelineOptions {
if (!cursor) {
const { cursor, hasLocalTail, initialTimelineLimit } = input;
if (!cursor || !hasLocalTail) {
return {
direction: "tail",
limit: DEFAULT_INITIAL_TIMELINE_LIMIT,
limit: initialTimelineLimit,
projection: "canonical",
};
}
@@ -40,8 +49,15 @@ function buildInitialTimelineRequest(
};
}
function resolveInitialTimelineLimit(): number {
return Platform.OS === "web"
? UNBOUNDED_TIMELINE_LIMIT
: NATIVE_INITIAL_TIMELINE_LIMIT;
}
export const __private__ = {
buildInitialTimelineRequest,
resolveInitialTimelineLimit,
};
export function useAgentInitialization({
@@ -76,7 +92,13 @@ export function useAgentInitialization({
const session = useSessionStore.getState().sessions[serverId];
const cursor = session?.agentTimelineCursor.get(agentId);
const timelineRequest = buildInitialTimelineRequest(cursor);
const hasLocalTail = (session?.agentStreamTail.get(agentId)?.length ?? 0) > 0;
const initialTimelineLimit = resolveInitialTimelineLimit();
const timelineRequest = buildInitialTimelineRequest({
cursor,
hasLocalTail,
initialTimelineLimit,
});
const initRequestDirection =
timelineRequest.direction === "after" ? "after" : "tail";
@@ -128,9 +150,10 @@ export function useAgentInitialization({
try {
await client.refreshAgent(agentId);
const initialTimelineLimit = resolveInitialTimelineLimit();
await client.fetchAgentTimeline(agentId, {
direction: "tail",
limit: DEFAULT_INITIAL_TIMELINE_LIMIT,
limit: initialTimelineLimit,
projection: "canonical",
});
} catch (error) {
@@ -57,6 +57,92 @@ const concatInt16 = (a: Int16Array, b: Int16Array): Int16Array => {
return out;
};
const int16ToFloat32 = (input: Int16Array): Float32Array => {
const out = new Float32Array(input.length);
for (let i = 0; i < input.length; i += 1) {
out[i] = input[i]! / 32768;
}
return out;
};
type Pcm16Wav = {
sampleRate: number;
samples: Int16Array;
};
const parsePcm16Wav = (buffer: ArrayBuffer): Pcm16Wav | null => {
if (buffer.byteLength < 44) {
return null;
}
const view = new DataView(buffer);
const readAscii = (offset: number, length: number): string => {
let out = "";
for (let i = 0; i < length; i += 1) {
out += String.fromCharCode(view.getUint8(offset + i));
}
return out;
};
if (readAscii(0, 4) !== "RIFF" || readAscii(8, 4) !== "WAVE") {
return null;
}
let offset = 12;
let channels = 0;
let sampleRate = 0;
let bitsPerSample = 0;
let dataOffset = 0;
let dataSize = 0;
while (offset + 8 <= buffer.byteLength) {
const chunkId = readAscii(offset, 4);
const chunkSize = view.getUint32(offset + 4, true);
const chunkDataOffset = offset + 8;
if (chunkDataOffset + chunkSize > buffer.byteLength) {
break;
}
if (chunkId === "fmt " && chunkSize >= 16) {
const audioFormat = view.getUint16(chunkDataOffset, true);
channels = view.getUint16(chunkDataOffset + 2, true);
sampleRate = view.getUint32(chunkDataOffset + 4, true);
bitsPerSample = view.getUint16(chunkDataOffset + 14, true);
if (audioFormat !== 1) {
return null;
}
} else if (chunkId === "data") {
dataOffset = chunkDataOffset;
dataSize = chunkSize;
break;
}
offset = chunkDataOffset + chunkSize + (chunkSize % 2);
}
if (!dataOffset || !dataSize || sampleRate <= 0 || bitsPerSample !== 16 || channels <= 0) {
return null;
}
const sampleCount = Math.floor(dataSize / 2);
const interleaved = new Int16Array(buffer, dataOffset, sampleCount);
if (channels === 1) {
return { sampleRate, samples: new Int16Array(interleaved) };
}
const frameCount = Math.floor(interleaved.length / channels);
const mono = new Int16Array(frameCount);
for (let frame = 0; frame < frameCount; frame += 1) {
let sum = 0;
for (let ch = 0; ch < channels; ch += 1) {
sum += interleaved[frame * channels + ch] ?? 0;
}
mono[frame] = Math.round(sum / channels);
}
return { sampleRate, samples: mono };
};
const int16ToBase64 = (pcm: Int16Array): string => {
const bytes = new Uint8Array(pcm.buffer, pcm.byteOffset, pcm.byteLength);
let binary = "";
@@ -359,15 +445,22 @@ export function useDictationAudioSource(config: DictationAudioSourceConfig): Dic
}
}
if (mode === "recorder" && recorder.stoppedPromise && context) {
if (mode === "recorder" && recorder.stoppedPromise) {
try {
const blob = await recorder.stoppedPromise;
const arrayBuffer = await blob.arrayBuffer();
if (arrayBuffer.byteLength > 0) {
const decoded = await decodeAudioData(context, arrayBuffer);
const floatPcm = decoded.getChannelData(0);
const pcm16 = resampleToPcm16(floatPcm, decoded.sampleRate, 16000);
emitPcmSegments(pcm16);
const parsedWav = parsePcm16Wav(arrayBuffer);
if (parsedWav) {
const floatPcm = int16ToFloat32(parsedWav.samples);
const pcm16 = resampleToPcm16(floatPcm, parsedWav.sampleRate, 16000);
emitPcmSegments(pcm16);
} else if (context) {
const decoded = await decodeAudioData(context, arrayBuffer);
const floatPcm = decoded.getChannelData(0);
const pcm16 = resampleToPcm16(floatPcm, decoded.sampleRate, 16000);
emitPcmSegments(pcm16);
}
}
} catch (err) {
onErrorRef.current?.(err instanceof Error ? err : new Error(String(err)));
+9 -5
View File
@@ -32,6 +32,7 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
const [duration, setDuration] = useState(0);
const [error, setError] = useState<string | null>(null);
const [status, setStatus] = useState<DictationStatus>("idle");
const latestPartialTranscriptRef = useRef("");
const onTranscriptRef = useRef(onTranscript);
useEffect(() => {
@@ -130,6 +131,7 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
const clearStreamingState = useCallback(() => {
senderRef.current?.clearAll();
latestPartialTranscriptRef.current = "";
setPartialTranscript("");
}, []);
@@ -181,6 +183,7 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
return;
}
const next = message.payload.text ?? "";
latestPartialTranscriptRef.current = next;
setPartialTranscript(next);
onPartialTranscriptRef.current?.(next, { requestId: generateMessageId() });
});
@@ -203,13 +206,14 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
(text: string, requestId: string) => {
setIsProcessing(false);
isProcessingRef.current = false;
setPartialTranscript("");
setDuration(0);
setStatus("idle");
const transcriptText = (
text.trim().length > 0 ? text.trim() : latestPartialTranscriptRef.current.trim()
);
clearStreamingState();
const transcriptText = text.trim();
if (!transcriptText) {
return;
}
@@ -261,14 +265,14 @@ export function useDictation(options: UseDictationOptions): UseDictationResult {
try {
await audio.start();
if (client?.isConnected) {
await startNewStream("start");
}
isRecordingRef.current = true;
setIsRecording(true);
if (enableDuration) {
startDurationTracking();
}
if (client?.isConnected) {
await startNewStream("start");
}
} catch (err) {
await audio.stop().catch(() => undefined);
stopDurationTracking();