feat: capture AI text responses on RUNNING->IDLE for Discord relay
This commit is contained in:
@@ -1416,6 +1416,9 @@ function setupMonitor() {
|
||||
let lastModTime = ''; // track lastModifiedTime to distinguish thinking vs approval
|
||||
let stallProbed = false; // prevent repeated step probes during same stall
|
||||
let lastRelayedTaskText = ''; // dedup TASK_BOUNDARY relay
|
||||
let wasRunning = false; // track RUNNING→IDLE transition for response capture
|
||||
let lastUserInputStepIdx = -1; // track user input for response matching
|
||||
let lastResponseCaptureStep = -1; // dedup: don't capture same response twice
|
||||
setInterval(async () => {
|
||||
pollCount++;
|
||||
if (pollCount <= 3 || pollCount % 12 === 0) {
|
||||
@@ -1782,6 +1785,59 @@ function setupMonitor() {
|
||||
else if (pollCount <= 5) {
|
||||
logToFile(`[TASK-STEP] null (no task step in session)`);
|
||||
}
|
||||
// ── RUNNING → IDLE transition: capture AI response for Discord ──
|
||||
const userInputIdx = bestSession.lastUserInputStepIndex ?? -1;
|
||||
if (userInputIdx > lastUserInputStepIdx) {
|
||||
lastUserInputStepIdx = userInputIdx;
|
||||
logToFile(`[RESPONSE-CAPTURE] user input detected at step ${userInputIdx}`);
|
||||
}
|
||||
if (wasRunning && !isRunning && currentCount > lastResponseCaptureStep) {
|
||||
logToFile(`[RESPONSE-CAPTURE] RUNNING→IDLE, steps=${currentCount}, capturing response...`);
|
||||
lastResponseCaptureStep = currentCount;
|
||||
try {
|
||||
const offset = Math.max(0, currentCount - 5);
|
||||
const latestResp = await sdk.ls.rawRPC('GetCascadeTrajectorySteps', {
|
||||
cascadeId: bestSessionId,
|
||||
stepOffset: offset,
|
||||
});
|
||||
if (latestResp?.steps?.length > 0) {
|
||||
const steps = latestResp.steps;
|
||||
for (let ri = steps.length - 1; ri >= 0; ri--) {
|
||||
const s = steps[ri];
|
||||
const sType = s?.type || '';
|
||||
if (sType.includes('PLANNER_RESPONSE') || sType.includes('MESSAGE')) {
|
||||
const parts = s?.content?.parts || s?.parts || [];
|
||||
let textContent = '';
|
||||
for (const p of parts) {
|
||||
if (p?.text)
|
||||
textContent += p.text;
|
||||
else if (typeof p === 'string')
|
||||
textContent += p;
|
||||
}
|
||||
if (!textContent && s?.metadata?.text)
|
||||
textContent = s.metadata.text;
|
||||
if (!textContent && s?.rawOutput)
|
||||
textContent = typeof s.rawOutput === 'string' ? s.rawOutput : JSON.stringify(s.rawOutput);
|
||||
if (textContent.length > 10) {
|
||||
logToFile(`[RESPONSE-CAPTURE] found ${sType} (${textContent.length} chars) at step ${offset + ri}`);
|
||||
const truncated = textContent.length > 1800
|
||||
? textContent.substring(0, 1800) + '\n\n_(이하 생략)_'
|
||||
: textContent;
|
||||
writeChatSnapshot(`💬 **AI 응답**\n\n${truncated}`);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
logToFile(`[RESPONSE-CAPTURE] ${sType} too short (${textContent.length}), keys=[${Object.keys(s).join(',')}]`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (re) {
|
||||
logToFile(`[RESPONSE-CAPTURE] error: ${re.message.substring(0, 100)}`);
|
||||
}
|
||||
}
|
||||
wasRunning = isRunning;
|
||||
}
|
||||
catch (e) {
|
||||
if (pollCount <= 5 || pollCount % 20 === 0) {
|
||||
|
||||
Reference in New Issue
Block a user