fix: use real API response (trajectoryId, current:true, step count diffing)

This commit is contained in:
2026-03-07 19:25:48 +09:00
parent e4b98af308
commit b6adeff402
4 changed files with 126 additions and 97 deletions

Binary file not shown.

View File

@@ -286,81 +286,91 @@ function activate(context) {
return; return;
} }
try { try {
// Get trajectory descriptions — lightweight list of all conversations // Get trajectory list — returns {trajectories:[{trajectoryId, trajectoryScope, current}]}
const result = await lsRPC('GetUserTrajectoryDescriptions'); const result = await lsRPC('GetUserTrajectoryDescriptions');
if (!result) { if (!result || !result.trajectories) {
return; return;
} }
// Debug: log structure on first call // Debug: log structure on first call
if (Object.keys(lastStepIndex).length === 0) { const isFirstPoll = Object.keys(lastStepIndex).length === 0;
console.log(`Gravity Bridge: [LS] trajectories response keys: ${typeof result === 'object' ? Object.keys(result).join(', ') : typeof result}`); if (isFirstPoll) {
console.log(`Gravity Bridge: [LS] trajectories response keys: ${Object.keys(result).join(', ')}`);
console.log(`Gravity Bridge: [LS] trajectories sample: ${JSON.stringify(result).substring(0, 600)}`); console.log(`Gravity Bridge: [LS] trajectories sample: ${JSON.stringify(result).substring(0, 600)}`);
} }
const trajectories = result.trajectories || result.trajectory_descriptions || result.userTrajectoryDescriptions || result.user_trajectory_descriptions || []; // Find current (active) trajectory
if (!Array.isArray(trajectories)) { const trajectories = result.trajectories;
const current = trajectories.find((t) => t.current === true) || trajectories[0];
if (!current?.trajectoryId) {
return; return;
} }
for (const traj of trajectories) { const trajId = current.trajectoryId;
const id = traj.googleAgentId || traj.google_agent_id || traj.id || ''; // Fetch actual steps to detect changes
const stepIdx = traj.lastStepIndex ?? traj.last_step_index ?? traj.step_count ?? 0; const stepsResult = await lsRPC('GetCascadeTrajectorySteps', {
const prev = lastStepIndex[id]; trajectoryId: trajId,
if (prev !== undefined && stepIdx > prev) {
// New steps! Fetch full trajectory to get AI response
console.log(`Gravity Bridge: [LS] ${id.substring(0, 8)} new steps: ${prev}${stepIdx}`);
const full = await lsRPC('GetCascadeTrajectorySteps', {
googleAgentId: id,
trajectoryId: traj.trajectoryId || traj.trajectory_id || '',
startStepIndex: prev,
}); });
// Also log structure on first fetch if (isFirstPoll && stepsResult) {
if (full) { console.log(`Gravity Bridge: [LS] steps response keys: ${typeof stepsResult === 'object' ? Object.keys(stepsResult).join(', ') : typeof stepsResult}`);
console.log(`Gravity Bridge: [LS] steps response keys: ${typeof full === 'object' ? Object.keys(full).join(', ') : typeof full}`); console.log(`Gravity Bridge: [LS] steps sample: ${JSON.stringify(stepsResult).substring(0, 800)}`);
console.log(`Gravity Bridge: [LS] steps sample: ${JSON.stringify(full).substring(0, 600)}`);
} }
if (full) { if (!stepsResult) {
extractAndRelay(full, prev, stepIdx); return;
} }
// Count steps — detect new ones
const steps = stepsResult.steps || stepsResult.cortexSteps || stepsResult.cortex_steps || [];
const stepCount = Array.isArray(steps) ? steps.length : 0;
const prevCount = lastStepIndex[trajId] ?? -1;
if (prevCount === -1) {
// First poll — just record count, don't relay old messages
lastStepIndex[trajId] = stepCount;
console.log(`Gravity Bridge: [LS] trajectory ${trajId.substring(0, 8)} initialized with ${stepCount} steps`);
return;
} }
lastStepIndex[id] = stepIdx; if (stepCount > prevCount) {
console.log(`Gravity Bridge: [LS] ${trajId.substring(0, 8)} new steps: ${prevCount}${stepCount}`);
// Extract AI text from new steps
const newSteps = steps.slice(prevCount);
extractAndRelaySteps(newSteps);
lastStepIndex[trajId] = stepCount;
} }
} }
catch (e) { catch (e) {
console.log(`Gravity Bridge: [LS poll] error: ${e}`); console.log(`Gravity Bridge: [LS poll] error: ${e}`);
} }
} }
function extractAndRelay(trajectory, fromStep, toStep) { function extractAndRelaySteps(steps) {
// Extract PlannerResponse or assistant messages from trajectory steps
const steps = trajectory.steps || trajectory.cortex_steps || [];
const messages = []; const messages = [];
for (const step of steps) { for (const step of steps) {
const idx = step.index ?? step.step_index ?? 0; // Try every possible way to find AI text in a step
if (idx <= fromStep) { const type = step.type || step.stepType || step.step_type || '';
const content = step.content || step.summary || step.text || step.message || '';
// PlannerResponse = AI's text output to user
if (content && (type.includes('Response') || type.includes('response') ||
type.includes('Message') || type.includes('message') ||
type.includes('Notify') || type.includes('notify'))) {
messages.push(content);
continue; continue;
} }
const type = step.type || step.step_type || ''; // Check nested data/content
const content = step.content || step.summary || step.text || '';
// PlannerResponse = AI's text output to user
if ((type === 'PlannerResponse' || type === 'planner_response') && content) {
messages.push(content);
}
// Also capture user-facing messages
if (step.data?.content && typeof step.data.content === 'string') { if (step.data?.content && typeof step.data.content === 'string') {
messages.push(step.data.content); messages.push(step.data.content);
continue;
} }
// Check role-based (assistant messages)
if ((step.role === 'assistant' || step.role === 'model') && content) {
messages.push(content);
continue;
} }
// Fallback: if no detailed steps, try messages array // Catch-all: any string content longer than 20 chars that's not a tool call
if (messages.length === 0) { if (typeof content === 'string' && content.length > 20 &&
const msgs = trajectory.messages || trajectory.chat_messages || []; !type.includes('Tool') && !type.includes('tool') &&
for (const msg of msgs) { !type.includes('Command') && !type.includes('command')) {
if (msg.role === 'assistant' && msg.content) { messages.push(content);
messages.push(msg.content);
}
} }
} }
if (messages.length > 0) { if (messages.length > 0) {
const combined = messages.join('\n\n---\n\n'); const combined = messages.join('\n\n---\n\n');
writeChatSnapshot(combined); writeChatSnapshot(combined);
console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) to Discord`); console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) (${combined.length} chars) to Discord`);
} }
} }
// Start LS bridge after a delay // Start LS bridge after a delay

File diff suppressed because one or more lines are too long

View File

@@ -266,88 +266,107 @@ export function activate(context: vscode.ExtensionContext) {
async function pollConversations() { async function pollConversations() {
if (!lsPort) { return; } if (!lsPort) { return; }
try { try {
// Get trajectory descriptions — lightweight list of all conversations // Get trajectory list — returns {trajectories:[{trajectoryId, trajectoryScope, current}]}
const result = await lsRPC('GetUserTrajectoryDescriptions'); const result = await lsRPC('GetUserTrajectoryDescriptions');
if (!result) { return; } if (!result || !result.trajectories) { return; }
// Debug: log structure on first call // Debug: log structure on first call
if (Object.keys(lastStepIndex).length === 0) { const isFirstPoll = Object.keys(lastStepIndex).length === 0;
console.log(`Gravity Bridge: [LS] trajectories response keys: ${typeof result === 'object' ? Object.keys(result).join(', ') : typeof result}`); if (isFirstPoll) {
console.log(`Gravity Bridge: [LS] trajectories response keys: ${Object.keys(result).join(', ')}`);
console.log(`Gravity Bridge: [LS] trajectories sample: ${JSON.stringify(result).substring(0, 600)}`); console.log(`Gravity Bridge: [LS] trajectories sample: ${JSON.stringify(result).substring(0, 600)}`);
} }
const trajectories = result.trajectories || result.trajectory_descriptions || result.userTrajectoryDescriptions || result.user_trajectory_descriptions || []; // Find current (active) trajectory
if (!Array.isArray(trajectories)) { return; } const trajectories = result.trajectories as any[];
const current = trajectories.find((t: any) => t.current === true) || trajectories[0];
if (!current?.trajectoryId) { return; }
for (const traj of trajectories) { const trajId = current.trajectoryId;
const id = traj.googleAgentId || traj.google_agent_id || traj.id || '';
const stepIdx = traj.lastStepIndex ?? traj.last_step_index ?? traj.step_count ?? 0;
const prev = lastStepIndex[id];
if (prev !== undefined && stepIdx > prev) { // Fetch actual steps to detect changes
// New steps! Fetch full trajectory to get AI response const stepsResult = await lsRPC('GetCascadeTrajectorySteps', {
console.log(`Gravity Bridge: [LS] ${id.substring(0, 8)} new steps: ${prev}${stepIdx}`); trajectoryId: trajId,
const full = await lsRPC('GetCascadeTrajectorySteps', {
googleAgentId: id,
trajectoryId: traj.trajectoryId || traj.trajectory_id || '',
startStepIndex: prev,
}); });
// Also log structure on first fetch
if (full) { if (isFirstPoll && stepsResult) {
console.log(`Gravity Bridge: [LS] steps response keys: ${typeof full === 'object' ? Object.keys(full).join(', ') : typeof full}`); console.log(`Gravity Bridge: [LS] steps response keys: ${typeof stepsResult === 'object' ? Object.keys(stepsResult).join(', ') : typeof stepsResult}`);
console.log(`Gravity Bridge: [LS] steps sample: ${JSON.stringify(full).substring(0, 600)}`); console.log(`Gravity Bridge: [LS] steps sample: ${JSON.stringify(stepsResult).substring(0, 800)}`);
} }
if (full) { if (!stepsResult) { return; }
extractAndRelay(full, prev, stepIdx);
// Count steps — detect new ones
const steps = stepsResult.steps || stepsResult.cortexSteps || stepsResult.cortex_steps || [];
const stepCount = Array.isArray(steps) ? steps.length : 0;
const prevCount = lastStepIndex[trajId] ?? -1;
if (prevCount === -1) {
// First poll — just record count, don't relay old messages
lastStepIndex[trajId] = stepCount;
console.log(`Gravity Bridge: [LS] trajectory ${trajId.substring(0, 8)} initialized with ${stepCount} steps`);
return;
} }
}
lastStepIndex[id] = stepIdx; if (stepCount > prevCount) {
console.log(`Gravity Bridge: [LS] ${trajId.substring(0, 8)} new steps: ${prevCount}${stepCount}`);
// Extract AI text from new steps
const newSteps = steps.slice(prevCount);
extractAndRelaySteps(newSteps);
lastStepIndex[trajId] = stepCount;
} }
} catch (e) { } catch (e) {
console.log(`Gravity Bridge: [LS poll] error: ${e}`); console.log(`Gravity Bridge: [LS poll] error: ${e}`);
} }
} }
function extractAndRelay(trajectory: any, fromStep: number, toStep: number) {
// Extract PlannerResponse or assistant messages from trajectory steps function extractAndRelaySteps(steps: any[]) {
const steps = trajectory.steps || trajectory.cortex_steps || [];
const messages: string[] = []; const messages: string[] = [];
for (const step of steps) { for (const step of steps) {
const idx = step.index ?? step.step_index ?? 0; // Try every possible way to find AI text in a step
if (idx <= fromStep) { continue; } const type = step.type || step.stepType || step.step_type || '';
const content = step.content || step.summary || step.text || step.message || '';
const type = step.type || step.step_type || '';
const content = step.content || step.summary || step.text || '';
// PlannerResponse = AI's text output to user // PlannerResponse = AI's text output to user
if ((type === 'PlannerResponse' || type === 'planner_response') && content) { if (content && (
type.includes('Response') || type.includes('response') ||
type.includes('Message') || type.includes('message') ||
type.includes('Notify') || type.includes('notify')
)) {
messages.push(content); messages.push(content);
} continue;
// Also capture user-facing messages
if (step.data?.content && typeof step.data.content === 'string') {
messages.push(step.data.content);
}
} }
// Fallback: if no detailed steps, try messages array // Check nested data/content
if (messages.length === 0) { if (step.data?.content && typeof step.data.content === 'string') {
const msgs = trajectory.messages || trajectory.chat_messages || []; messages.push(step.data.content);
for (const msg of msgs) { continue;
if (msg.role === 'assistant' && msg.content) {
messages.push(msg.content);
} }
// Check role-based (assistant messages)
if ((step.role === 'assistant' || step.role === 'model') && content) {
messages.push(content);
continue;
}
// Catch-all: any string content longer than 20 chars that's not a tool call
if (typeof content === 'string' && content.length > 20 &&
!type.includes('Tool') && !type.includes('tool') &&
!type.includes('Command') && !type.includes('command')) {
messages.push(content);
} }
} }
if (messages.length > 0) { if (messages.length > 0) {
const combined = messages.join('\n\n---\n\n'); const combined = messages.join('\n\n---\n\n');
writeChatSnapshot(combined); writeChatSnapshot(combined);
console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) to Discord`); console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) (${combined.length} chars) to Discord`);
} }
} }
// Start LS bridge after a delay // Start LS bridge after a delay
setTimeout(async () => { setTimeout(async () => {
const found = await discoverLS(); const found = await discoverLS();