feat: subscribeToStream on new cascade — real-time StreamCascadeReactiveUpdates subscription

This commit is contained in:
2026-03-07 23:23:16 +09:00
parent 12131b9103
commit 87094e00b0
4 changed files with 124 additions and 169 deletions

Binary file not shown.

View File

@@ -334,6 +334,7 @@ function activate(context) {
else if (stepIdx > 0 && summary) { else if (stepIdx > 0 && summary) {
// New conversation with AI response! // New conversation with AI response!
console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`);
subscribeToStream(key);
// Try to extract AI text from extensionLogs // Try to extract AI text from extensionLogs
const aiText = extractFromLogs(parsed); const aiText = extractFromLogs(parsed);
if (aiText) { if (aiText) {
@@ -435,96 +436,71 @@ function activate(context) {
} }
return null; return null;
} }
// ========== Trial D2: Streaming RPC with protocol_version=1 ========== // ========== Stream subscription for active cascades ==========
setTimeout(async () => { function subscribeToStream(cascadeId) {
if (!lsPort || !lsCsrf) { if (!lsPort || !lsCsrf) {
console.log('Gravity Bridge: [Trial D2] Skipped — no LS');
return; return;
} }
console.log(`Gravity Bridge: [Trial D2] Streaming RPCs with proto_version=1...`);
const http = require('http'); const http = require('http');
function tryProtoRPC(method, protoBody, timeout = 12000) { console.log(`Gravity Bridge: [Stream] Subscribing to cascade ${cascadeId.substring(0, 8)}...`);
return new Promise((resolve) => { const gidBuf = Buffer.from(cascadeId, 'utf-8');
// ConnectRPC frame: [flag(1)] [length(4 big-endian)] [message] // field1=version(varint=1), field2=cascadeId(string)
const frame = Buffer.alloc(5 + protoBody.length); const proto = Buffer.alloc(2 + 2 + gidBuf.length);
frame[0] = 0x00; // no compression proto[0] = 0x08;
frame.writeUInt32BE(protoBody.length, 1); proto[1] = 0x01;
protoBody.copy(frame, 5); proto[2] = 0x12;
const req = http.request({ proto[3] = gidBuf.length;
hostname: '127.0.0.1', port: lsPort, gidBuf.copy(proto, 4);
path: `/exa.language_server_pb.LanguageServerService/${method}`, // ConnectRPC frame
method: 'POST', const frame = Buffer.alloc(5 + proto.length);
headers: { frame[0] = 0x00;
'Content-Type': 'application/connect+proto', frame.writeUInt32BE(proto.length, 1);
'Connect-Protocol-Version': '1', proto.copy(frame, 5);
'x-codeium-csrf-token': lsCsrf, const req = http.request({
}, hostname: '127.0.0.1', port: lsPort,
timeout: timeout path: '/exa.language_server_pb.LanguageServerService/StreamCascadeReactiveUpdates',
}, (res) => { method: 'POST',
const chunks = []; headers: {
res.on('data', (c) => chunks.push(c)); 'Content-Type': 'application/connect+proto',
res.on('end', () => { 'Connect-Protocol-Version': '1',
const buf = Buffer.concat(chunks); 'x-codeium-csrf-token': lsCsrf,
// Skip ConnectRPC frame header (5 bytes) to get proto/JSON content },
const text = buf.toString('utf-8'); timeout: 30000
resolve(text); }, (res) => {
}); console.log(`Gravity Bridge: [Stream] Connected! status=${res.statusCode}`);
}); let totalBytes = 0;
req.on('error', (e) => resolve(`err:${e.message}`)); const allChunks = [];
req.on('timeout', () => { req.destroy(); resolve('timeout(stream open)'); }); res.on('data', (chunk) => {
req.write(frame); allChunks.push(chunk);
req.end(); totalBytes += chunk.length;
// Log each chunk as it arrives (real-time streaming)
const text = chunk.toString('utf-8');
console.log(`Gravity Bridge: [Stream] chunk (${chunk.length}B): ${text.substring(0, 300)}`);
}); });
} res.on('end', () => {
// Get latest trajectory ID first, then try streams WITH cascadeId const fullBuf = Buffer.concat(allChunks);
try { const fullText = fullBuf.toString('utf-8');
const dRaw = await vscode.commands.executeCommand('antigravity.getDiagnostics'); console.log(`Gravity Bridge: [Stream] Ended. Total ${totalBytes}B in ${allChunks.length} chunks`);
const d = typeof dRaw === 'string' ? JSON.parse(dRaw) : dRaw; // Try to extract readable text from the stream data
const ts = d?.recentTrajectories || []; // Look for strings in the protobuf data
if (ts.length > 0) { const readable = fullText.replace(/[\x00-\x1F\x7F-\x9F]/g, ' ').replace(/\s+/g, ' ').trim();
const latest = ts[ts.length - 1]; if (readable.length > 20) {
const gid = latest.googleAgentId || ''; console.log(`Gravity Bridge: [Stream] Readable: ${readable.substring(0, 1000)}`);
console.log(`Gravity Bridge: [Trial D3] Using cascade ${gid.substring(0, 8)} step=${latest.lastStepIndex}`); }
const gidBuf = Buffer.from(gid, 'utf-8'); // Check for error messages
// Attempt 1: field1=version(varint=1), field2=cascadeId(string) if (fullText.includes('"error"')) {
// 0x08 0x01 0x12 <len> <cascadeId> console.log(`Gravity Bridge: [Stream] Error in response: ${fullText.substring(0, 500)}`);
const proto1 = Buffer.alloc(2 + 2 + gidBuf.length); }
proto1[0] = 0x08; });
proto1[1] = 0x01; // field1 varint = 1 });
proto1[2] = 0x12; req.on('error', (e) => console.log(`Gravity Bridge: [Stream] Error: ${e.message}`));
proto1[3] = gidBuf.length; // field2 string req.on('timeout', () => {
gidBuf.copy(proto1, 4); console.log(`Gravity Bridge: [Stream] Timeout (30s) — closing`);
let r = await tryProtoRPC('StreamCascadeReactiveUpdates', proto1); req.destroy();
console.log(`Gravity Bridge: [Trial D3] StreamCascade(v1+cascade): ${r.substring(0, 800)}`); });
// Attempt 2: field1=cascadeId(string), field2=version(varint=1) req.write(frame);
// 0x0A <len> <cascadeId> 0x10 0x01 req.end();
const proto2 = Buffer.alloc(2 + gidBuf.length + 2); }
proto2[0] = 0x0A;
proto2[1] = gidBuf.length; // field1 string
gidBuf.copy(proto2, 2);
proto2[2 + gidBuf.length] = 0x10; // field2 varint
proto2[3 + gidBuf.length] = 0x01;
r = await tryProtoRPC('StreamCascadeReactiveUpdates', proto2);
console.log(`Gravity Bridge: [Trial D3] StreamCascade(cascade+v1): ${r.substring(0, 800)}`);
// Attempt 3: StreamCascadeSummariesReactiveUpdates with version+cascadeId
r = await tryProtoRPC('StreamCascadeSummariesReactiveUpdates', proto1);
console.log(`Gravity Bridge: [Trial D3] StreamSummaries(v1+cascade): ${r.substring(0, 800)}`);
// Attempt 4: GetCascadeTrajectorySteps with proto
const stepIdx = Math.max(0, latest.lastStepIndex - 1);
const stepsProto = Buffer.alloc(2 + gidBuf.length + 2);
stepsProto[0] = 0x0A;
stepsProto[1] = gidBuf.length;
gidBuf.copy(stepsProto, 2);
stepsProto[2 + gidBuf.length] = 0x10;
stepsProto[3 + gidBuf.length] = stepIdx;
r = await tryProtoRPC('GetCascadeTrajectorySteps', stepsProto);
console.log(`Gravity Bridge: [Trial D3] Steps(proto): ${r.substring(0, 1000)}`);
}
}
catch (e) {
console.log(`Gravity Bridge: [Trial D3] err: ${e.message}`);
}
}, 15000);
// Start LS bridge after a delay // Start LS bridge after a delay
setTimeout(async () => { setTimeout(async () => {
const found = await discoverLS(); const found = await discoverLS();

File diff suppressed because one or more lines are too long

View File

@@ -313,7 +313,7 @@ export function activate(context: vscode.ExtensionContext) {
} else if (stepIdx > 0 && summary) { } else if (stepIdx > 0 && summary) {
// New conversation with AI response! // New conversation with AI response!
console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`);
subscribeToStream(key);
// Try to extract AI text from extensionLogs // Try to extract AI text from extensionLogs
const aiText = extractFromLogs(parsed); const aiText = extractFromLogs(parsed);
if (aiText) { if (aiText) {
@@ -429,95 +429,74 @@ export function activate(context: vscode.ExtensionContext) {
// ========== Trial D2: Streaming RPC with protocol_version=1 ========== // ========== Stream subscription for active cascades ==========
setTimeout(async () => { function subscribeToStream(cascadeId: string): void {
if (!lsPort || !lsCsrf) { console.log('Gravity Bridge: [Trial D2] Skipped — no LS'); return; } if (!lsPort || !lsCsrf) { return; }
console.log(`Gravity Bridge: [Trial D2] Streaming RPCs with proto_version=1...`);
const http = require('http'); const http = require('http');
console.log(`Gravity Bridge: [Stream] Subscribing to cascade ${cascadeId.substring(0, 8)}...`);
function tryProtoRPC(method: string, protoBody: Buffer, timeout: number = 12000): Promise<string> { const gidBuf = Buffer.from(cascadeId, 'utf-8');
return new Promise((resolve) => { // field1=version(varint=1), field2=cascadeId(string)
// ConnectRPC frame: [flag(1)] [length(4 big-endian)] [message] const proto = Buffer.alloc(2 + 2 + gidBuf.length);
const frame = Buffer.alloc(5 + protoBody.length); proto[0] = 0x08; proto[1] = 0x01;
frame[0] = 0x00; // no compression proto[2] = 0x12; proto[3] = gidBuf.length;
frame.writeUInt32BE(protoBody.length, 1); gidBuf.copy(proto, 4);
protoBody.copy(frame, 5);
const req = http.request({ // ConnectRPC frame
hostname: '127.0.0.1', port: lsPort, const frame = Buffer.alloc(5 + proto.length);
path: `/exa.language_server_pb.LanguageServerService/${method}`, frame[0] = 0x00;
method: 'POST', frame.writeUInt32BE(proto.length, 1);
headers: { proto.copy(frame, 5);
'Content-Type': 'application/connect+proto',
'Connect-Protocol-Version': '1', const req = http.request({
'x-codeium-csrf-token': lsCsrf, hostname: '127.0.0.1', port: lsPort,
}, path: '/exa.language_server_pb.LanguageServerService/StreamCascadeReactiveUpdates',
timeout: timeout method: 'POST',
}, (res: any) => { headers: {
const chunks: Buffer[] = []; 'Content-Type': 'application/connect+proto',
res.on('data', (c: Buffer) => chunks.push(c)); 'Connect-Protocol-Version': '1',
res.on('end', () => { 'x-codeium-csrf-token': lsCsrf,
const buf = Buffer.concat(chunks); },
// Skip ConnectRPC frame header (5 bytes) to get proto/JSON content timeout: 30000
const text = buf.toString('utf-8'); }, (res: any) => {
resolve(text); console.log(`Gravity Bridge: [Stream] Connected! status=${res.statusCode}`);
}); let totalBytes = 0;
}); const allChunks: Buffer[] = [];
req.on('error', (e: any) => resolve(`err:${e.message}`));
req.on('timeout', () => { req.destroy(); resolve('timeout(stream open)'); }); res.on('data', (chunk: Buffer) => {
req.write(frame); req.end(); allChunks.push(chunk);
totalBytes += chunk.length;
// Log each chunk as it arrives (real-time streaming)
const text = chunk.toString('utf-8');
console.log(`Gravity Bridge: [Stream] chunk (${chunk.length}B): ${text.substring(0, 300)}`);
}); });
}
// Get latest trajectory ID first, then try streams WITH cascadeId res.on('end', () => {
try { const fullBuf = Buffer.concat(allChunks);
const dRaw = await vscode.commands.executeCommand('antigravity.getDiagnostics'); const fullText = fullBuf.toString('utf-8');
const d = typeof dRaw === 'string' ? JSON.parse(dRaw) : dRaw; console.log(`Gravity Bridge: [Stream] Ended. Total ${totalBytes}B in ${allChunks.length} chunks`);
const ts = d?.recentTrajectories || [];
if (ts.length > 0) {
const latest = ts[ts.length - 1];
const gid = latest.googleAgentId || '';
console.log(`Gravity Bridge: [Trial D3] Using cascade ${gid.substring(0, 8)} step=${latest.lastStepIndex}`);
const gidBuf = Buffer.from(gid, 'utf-8'); // Try to extract readable text from the stream data
// Look for strings in the protobuf data
const readable = fullText.replace(/[\x00-\x1F\x7F-\x9F]/g, ' ').replace(/\s+/g, ' ').trim();
if (readable.length > 20) {
console.log(`Gravity Bridge: [Stream] Readable: ${readable.substring(0, 1000)}`);
}
// Attempt 1: field1=version(varint=1), field2=cascadeId(string) // Check for error messages
// 0x08 0x01 0x12 <len> <cascadeId> if (fullText.includes('"error"')) {
const proto1 = Buffer.alloc(2 + 2 + gidBuf.length); console.log(`Gravity Bridge: [Stream] Error in response: ${fullText.substring(0, 500)}`);
proto1[0] = 0x08; proto1[1] = 0x01; // field1 varint = 1 }
proto1[2] = 0x12; proto1[3] = gidBuf.length; // field2 string });
gidBuf.copy(proto1, 4); });
req.on('error', (e: any) => console.log(`Gravity Bridge: [Stream] Error: ${e.message}`));
let r = await tryProtoRPC('StreamCascadeReactiveUpdates', proto1); req.on('timeout', () => {
console.log(`Gravity Bridge: [Trial D3] StreamCascade(v1+cascade): ${r.substring(0, 800)}`); console.log(`Gravity Bridge: [Stream] Timeout (30s) — closing`);
req.destroy();
// Attempt 2: field1=cascadeId(string), field2=version(varint=1) });
// 0x0A <len> <cascadeId> 0x10 0x01 req.write(frame);
const proto2 = Buffer.alloc(2 + gidBuf.length + 2); req.end();
proto2[0] = 0x0A; proto2[1] = gidBuf.length; // field1 string }
gidBuf.copy(proto2, 2);
proto2[2 + gidBuf.length] = 0x10; // field2 varint
proto2[3 + gidBuf.length] = 0x01;
r = await tryProtoRPC('StreamCascadeReactiveUpdates', proto2);
console.log(`Gravity Bridge: [Trial D3] StreamCascade(cascade+v1): ${r.substring(0, 800)}`);
// Attempt 3: StreamCascadeSummariesReactiveUpdates with version+cascadeId
r = await tryProtoRPC('StreamCascadeSummariesReactiveUpdates', proto1);
console.log(`Gravity Bridge: [Trial D3] StreamSummaries(v1+cascade): ${r.substring(0, 800)}`);
// Attempt 4: GetCascadeTrajectorySteps with proto
const stepIdx = Math.max(0, latest.lastStepIndex - 1);
const stepsProto = Buffer.alloc(2 + gidBuf.length + 2);
stepsProto[0] = 0x0A; stepsProto[1] = gidBuf.length;
gidBuf.copy(stepsProto, 2);
stepsProto[2 + gidBuf.length] = 0x10;
stepsProto[3 + gidBuf.length] = stepIdx;
r = await tryProtoRPC('GetCascadeTrajectorySteps', stepsProto);
console.log(`Gravity Bridge: [Trial D3] Steps(proto): ${r.substring(0, 1000)}`);
}
} catch (e: any) { console.log(`Gravity Bridge: [Trial D3] err: ${e.message}`); }
}, 15000);
// Start LS bridge after a delay // Start LS bridge after a delay