Add page data delivery and publication guards
This commit is contained in:
@@ -112,49 +112,63 @@ function buildTempUser() {
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForSessionFinish(fetchImpl, baseUrl, sessionId, cookie, timeoutMs, runTrigger) {
|
||||
const response = await fetchImpl(`${baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/events`, {
|
||||
async function waitForAgentRunCompletion(fetchImpl, baseUrl, cookie, timeoutMs, createRun) {
|
||||
const { runId, sessionId } = await createRun();
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
const seen = [];
|
||||
|
||||
const response = await fetchImpl(`${baseUrl}/api/agent/runs/${encodeURIComponent(runId)}/events`, {
|
||||
headers: {
|
||||
Accept: 'text/event-stream',
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
if (!response.ok || !response.body) {
|
||||
const payload = await parseResponseBody(response);
|
||||
throw new Error(`session events failed: ${response.status} ${payload.text}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
const seen = [];
|
||||
const runId = await runTrigger();
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const chunks = buffer.split('\n\n');
|
||||
buffer = chunks.pop() ?? '';
|
||||
for (const chunk of chunks) {
|
||||
const trimmed = chunk.trim();
|
||||
if (!trimmed) continue;
|
||||
seen.push(trimmed);
|
||||
if (trimmed.includes('type":"Error"')) {
|
||||
throw new Error(`session stream error: ${trimmed}`);
|
||||
}
|
||||
if (trimmed.includes('type":"Finish"')) {
|
||||
await reader.cancel().catch(() => {});
|
||||
return { runId, seen };
|
||||
if (response.ok && response.body) {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
while (Date.now() < deadline) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const chunks = buffer.split('\n\n');
|
||||
buffer = chunks.pop() ?? '';
|
||||
for (const chunk of chunks) {
|
||||
const trimmed = chunk.trim();
|
||||
if (!trimmed || trimmed.startsWith(':')) continue;
|
||||
seen.push(trimmed);
|
||||
if (trimmed.includes('"status":"failed"')) {
|
||||
await reader.cancel().catch(() => {});
|
||||
return { runId, sessionId, seen, terminalStatus: 'failed' };
|
||||
}
|
||||
if (trimmed.includes('"status":"succeeded"')) {
|
||||
await reader.cancel().catch(() => {});
|
||||
return { runId, sessionId, seen, terminalStatus: 'succeeded' };
|
||||
}
|
||||
}
|
||||
}
|
||||
await reader.cancel().catch(() => {});
|
||||
}
|
||||
|
||||
await reader.cancel().catch(() => {});
|
||||
throw new Error(`session stream timeout after ${timeoutMs}ms`);
|
||||
while (Date.now() < deadline) {
|
||||
const run = await requestJson(fetchImpl, `${baseUrl}/api/agent/runs/${encodeURIComponent(runId)}`, {
|
||||
headers: { Cookie: cookie },
|
||||
timeoutMs: Math.min(10000, timeoutMs),
|
||||
});
|
||||
const status = run.json?.run?.status ?? null;
|
||||
const resolvedSessionId = run.json?.run?.agent_session_id ?? sessionId;
|
||||
if (status === 'succeeded' || status === 'failed') {
|
||||
return { runId, sessionId: resolvedSessionId, seen, terminalStatus: status };
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
}
|
||||
|
||||
throw new Error(`agent run timeout after ${timeoutMs}ms`);
|
||||
}
|
||||
|
||||
export { waitForAgentRunCompletion };
|
||||
|
||||
export async function runMemoryV2SessionFlowCli({
|
||||
argv = process.argv.slice(2),
|
||||
env = process.env,
|
||||
@@ -219,10 +233,9 @@ export async function runMemoryV2SessionFlowCli({
|
||||
}
|
||||
|
||||
const requestId = crypto.randomUUID();
|
||||
const { runId, seen } = await waitForSessionFinish(
|
||||
const { runId, sessionId: activeSessionId, seen, terminalStatus } = await waitForAgentRunCompletion(
|
||||
fetchImpl,
|
||||
options.baseUrl,
|
||||
sessionId,
|
||||
cookie,
|
||||
options.timeoutMs,
|
||||
async () => {
|
||||
@@ -248,14 +261,18 @@ export async function runMemoryV2SessionFlowCli({
|
||||
if (!created.ok || created.status !== 202 || !created.json?.run?.id) {
|
||||
throw new Error(`agent run failed: ${created.status} ${created.text}`);
|
||||
}
|
||||
return created.json.run.id;
|
||||
return {
|
||||
runId: created.json.run.id,
|
||||
sessionId: created.json?.run?.agent_session_id ?? sessionId,
|
||||
};
|
||||
},
|
||||
);
|
||||
checks.push(makeCheck('session_finish_seen', seen.some((chunk) => chunk.includes('type":"Finish"')), {
|
||||
checks.push(makeCheck('agent_run_terminal', terminalStatus === 'succeeded', {
|
||||
terminalStatus,
|
||||
eventCount: seen.length,
|
||||
}));
|
||||
|
||||
const detail = await requestJson(fetchImpl, `${options.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`, {
|
||||
const detail = await requestJson(fetchImpl, `${options.baseUrl}/api/sessions/${encodeURIComponent(activeSessionId)}`, {
|
||||
headers: { Cookie: cookie },
|
||||
timeoutMs: options.timeoutMs,
|
||||
});
|
||||
@@ -272,17 +289,22 @@ export async function runMemoryV2SessionFlowCli({
|
||||
const remember = await requestJson(fetchImpl, `${options.baseUrl}/api/user-memory/v1/remember-recent`, {
|
||||
method: 'POST',
|
||||
headers: { Cookie: cookie },
|
||||
body: { sessionId },
|
||||
body: { sessionId: activeSessionId },
|
||||
timeoutMs: options.timeoutMs,
|
||||
});
|
||||
checks.push(makeCheck('remember_recent_ok', remember.ok && remember.json?.ok === true, {
|
||||
status: remember.status,
|
||||
}));
|
||||
checks.push(makeCheck('remember_recent_extracted', remember.ok && Number(remember.json?.analyzed ?? 0) > 0, {
|
||||
status: remember.status,
|
||||
analyzed: remember.json?.analyzed ?? 0,
|
||||
memories: remember.json?.memories ?? 0,
|
||||
}));
|
||||
|
||||
const sync = await requestJson(fetchImpl, `${options.baseUrl}/api/user-memory/v1/sync`, {
|
||||
method: 'POST',
|
||||
headers: { Cookie: cookie },
|
||||
body: { sessionId },
|
||||
body: { sessionId: activeSessionId },
|
||||
timeoutMs: options.timeoutMs,
|
||||
});
|
||||
checks.push(makeCheck('sync_ok', sync.ok && sync.json?.ok === true, {
|
||||
@@ -310,7 +332,7 @@ export async function runMemoryV2SessionFlowCli({
|
||||
ok: checks.every((item) => item.ok),
|
||||
baseUrl: options.baseUrl,
|
||||
user: user.username,
|
||||
sessionId,
|
||||
sessionId: activeSessionId,
|
||||
runId,
|
||||
summary: {
|
||||
assistantPreview: assistantMessages.at(-1)?.content?.[0]?.text?.slice?.(0, 200) ?? null,
|
||||
@@ -326,6 +348,7 @@ export async function runMemoryV2SessionFlowCli({
|
||||
}
|
||||
: null,
|
||||
sessionEventsTail: seen.slice(-4),
|
||||
terminalStatus,
|
||||
},
|
||||
checks,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user