feat: support conversation state events in JavaScript sessions

This commit is contained in:
shiyue
2026-08-27 22:16:33 +08:00
parent e62a545569
commit 97b4a32dda
12 changed files with 458 additions and 11 deletions

View File

@@ -0,0 +1,18 @@
{
"runId": "86e8f20f-3e55-4ca6-9def-57b79725b4fc",
"sourceConversationId": "c01c2e84-5d52-4b0b-bab0-e10daf6c65c9",
"scriptsDir": "/home/cc-web/.ccweb/scripts",
"name": "conversation-idle-listener-e2e-20260827.js",
"scriptPath": "/home/cc-web/.ccweb/scripts/conversation-idle-listener-e2e-20260827.js",
"status": "succeeded",
"startedAt": "2026-08-27T14:04:55.516Z",
"finishedAt": "2026-08-27T14:05:46.085Z",
"durationMs": 50569,
"exitCode": 0,
"signal": null,
"terminationReason": null,
"stopRequested": false,
"tokenRevoked": true,
"pid": 576697,
"stderrPreview": ""
}

View File

@@ -0,0 +1 @@
{"sourceConversationId":"c01c2e84-5d52-4b0b-bab0-e10daf6c65c9","conversationId":"43c081dd-2c8d-431c-b75d-3e40dc04806e","initialStatus":"idle","statusDuringFirstTurn":"running","firstMessage":"未完成","continued":true,"continuationMessage":"任务完成","finalStatus":"idle","finalMessage":"任务完成","childIncluded":true,"idleEventCount":2,"events":[{"conversationId":"43c081dd-2c8d-431c-b75d-3e40dc04806e","event":"idle","previousStatus":"running","status":"idle","occurredAt":"2026-08-27T14:05:31.297Z","lastMessage":"未完成"},{"conversationId":"43c081dd-2c8d-431c-b75d-3e40dc04806e","event":"idle","previousStatus":"running","status":"idle","occurredAt":"2026-08-27T14:05:43.829Z","lastMessage":"任务完成"}]}

View File

@@ -0,0 +1,88 @@
import {
getCurrentConversationId,
createConversation,
sendMessage,
getLastMessage,
getConversationStatus,
getChildConversationIds,
onConversationEvent,
} from '@ccweb/session';
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const sourceConversationId = await getCurrentConversationId();
const conversationId = await createConversation(
'这是会话事件测试,请只回复:测试对话已准备'
);
const initialStatus = await getConversationStatus(conversationId);
const childrenBefore = await getChildConversationIds(sourceConversationId);
if (!childrenBefore.includes(conversationId)) {
throw Object.assign(new Error('新对话未出现在来源对话的直接子对话 ID 中'), {
code: 'child_conversation_missing',
});
}
const events = [];
let continued = false;
let continuationMessage = '';
let unsubscribe = () => {};
let finish;
let fail;
const completed = new Promise((resolve, reject) => {
finish = resolve;
fail = reject;
});
const timeout = setTimeout(() => {
unsubscribe();
fail(Object.assign(new Error('等待 idle 自动继续闭环超时'), {
code: 'idle_listener_timeout',
}));
}, 180000);
unsubscribe = await onConversationEvent(conversationId, 'idle', async (event) => {
const lastMessage = await getLastMessage(conversationId);
events.push({ ...event, lastMessage });
if (!continued && lastMessage.includes('未完成')) {
continued = true;
continuationMessage = await sendMessage(
conversationId,
'请继续完成任务,并且只回复:任务完成'
);
return;
}
if (continued && lastMessage.includes('任务完成')) {
unsubscribe();
clearTimeout(timeout);
finish();
}
});
const firstTurnPromise = sendMessage(
conversationId,
'开始执行第一阶段,并且只回复:未完成'
);
await delay(150);
const statusDuringFirstTurn = await getConversationStatus(conversationId);
const firstMessage = await firstTurnPromise;
await completed;
const finalStatus = await getConversationStatus(conversationId);
const finalMessage = await getLastMessage(conversationId);
const childrenAfter = await getChildConversationIds(sourceConversationId);
console.log(JSON.stringify({
sourceConversationId,
conversationId,
initialStatus,
statusDuringFirstTurn,
firstMessage,
continued,
continuationMessage,
finalStatus,
finalMessage,
childIncluded: childrenAfter.includes(conversationId),
idleEventCount: events.length,
events,
}));