"use strict"; /** * Gravity Bridge — VS Code Extension (SDK Edition) * * Uses antigravity-sdk for: * - Real-time step/conversation monitoring via EventMonitor * - Full conversation content via LSBridge.getConversation() * - Message sending via CascadeManager.sendPrompt() * - Accept/Reject via CascadeManager.acceptStep()/rejectStep() * * Communication with Discord via file-based bridge protocol. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.activate = activate; exports.deactivate = deactivate; const vscode = __importStar(require("vscode")); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const cp = __importStar(require("child_process")); // antigravity-sdk embedded locally (src/sdk/) let AntigravitySDK; let sdk; let statusBar; let bridgePath; let projectName; let isActive = false; let watcher = null; let commandsWatcher = null; const sentPendingIds = new Set(); // ─── Project Detection ─── function detectProjectName() { const config = vscode.workspace.getConfiguration('gravityBridge'); const configName = config.get('projectName'); if (configName) { return configName; } const folders = vscode.workspace.workspaceFolders; if (folders && folders.length > 0) { const cwd = folders[0].uri.fsPath; try { const remoteUrl = cp.execSync('git remote get-url origin', { cwd, encoding: 'utf-8', timeout: 3000 }).trim(); const match = remoteUrl.match(/\/([^\/]+?)(?:\.git)?$/); if (match && match[1]) { return match[1].toLowerCase().replace(/[\s\-]+/g, '_'); } } catch { } return path.basename(cwd).toLowerCase().replace(/[\s\-]+/g, '_'); } return 'default'; } // ─── Bridge File I/O ─── function ensureBridgeDir() { const dirs = ['', 'response', 'commands', 'chat_snapshots']; for (const d of dirs) { const p = path.join(bridgePath, d); if (!fs.existsSync(p)) { fs.mkdirSync(p, { recursive: true }); } } } function writeChatSnapshot(text) { try { // Write to chat_snapshots/*.json for Bot's chat_snapshot_scanner to pick up const snapshotDir = path.join(bridgePath, 'chat_snapshots'); if (!fs.existsSync(snapshotDir)) { fs.mkdirSync(snapshotDir, { recursive: true }); } const id = Date.now().toString(); const data = { id, project_name: projectName, content: text, timestamp: Date.now() / 1000, }; const filePath = path.join(snapshotDir, `${id}.json`); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); console.log(`Gravity Bridge: chat snapshot written (${text.length} chars) → ${id}.json`); } catch (e) { console.log(`Gravity Bridge: snapshot write error: ${e.message}`); } } function writePendingApproval(data) { try { const filePath = path.join(bridgePath, 'response', 'pending_approval.json'); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); } catch { } } // ─── Command File Watcher (Discord → Antigravity) ─── function processCommandFile(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); const cmd = JSON.parse(content); // Skip already consumed commands if (cmd.consumed) { try { fs.unlinkSync(filePath); } catch { } return; } // Ignore commands for other projects if (cmd.project_name && cmd.project_name !== projectName) { console.log(`Gravity Bridge: skipping command for "${cmd.project_name}" (we are "${projectName}")`); return; } // Bot writes 'text' field, not 'message' const text = cmd.text || cmd.message || ''; const action = cmd.action || ''; console.log(`Gravity Bridge: command — text="${text}" action="${action}"`); if (action === 'approve' && sdk) { sdk.cascade.acceptStep().catch((e) => console.log(`Gravity Bridge: approve error: ${e.message}`)); } else if (action === 'reject' && sdk) { sdk.cascade.rejectStep().catch((e) => console.log(`Gravity Bridge: reject error: ${e.message}`)); } else if (action === 'approve_terminal' && sdk) { sdk.cascade.acceptTerminalCommand().catch((e) => console.log(`Gravity Bridge: approve_terminal error: ${e.message}`)); } else if (text === '!stop') { // Cancel current operation vscode.commands.executeCommand('antigravity.agent.rejectAgentStep') .then(() => console.log('Gravity Bridge: ✅ stop sent'), () => { }); } else if (text.startsWith('!auto ')) { // Auto-approve mode toggle const mode = text.includes('on') ? 'true' : 'false'; console.log(`Gravity Bridge: auto-approve → ${mode}`); } else if (text) { // Send message to Antigravity — use VS Code command (most reliable) vscode.commands.executeCommand('antigravity.sendPromptToAgentPanel', text) .then(() => console.log(`Gravity Bridge: ✅ sent "${text.substring(0, 50)}" via sendPromptToAgentPanel`), (e) => console.log(`Gravity Bridge: sendPrompt failed: ${e.message}`)); } // Remove processed command file try { fs.unlinkSync(filePath); } catch { } } catch (e) { console.log(`Gravity Bridge: command processing error: ${e.message}`); } } function watchCommandsDir() { const cmdDir = path.join(bridgePath, 'commands'); // Process existing files try { for (const f of fs.readdirSync(cmdDir)) { if (f.endsWith('.json')) { processCommandFile(path.join(cmdDir, f)); } } } catch { } // Watch for new files try { commandsWatcher = fs.watch(cmdDir, (event, filename) => { if (filename && filename.endsWith('.json') && event === 'rename') { const fp = path.join(cmdDir, filename); if (fs.existsSync(fp)) { setTimeout(() => processCommandFile(fp), 200); } } }); } catch { } } // ─── SDK Integration ─── async function initSDK(context) { try { const sdkModule = require('./sdk/index'); AntigravitySDK = sdkModule.AntigravitySDK; } catch (err) { console.log(`Gravity Bridge: antigravity-sdk load failed: ${err.message}`); return false; } try { sdk = new AntigravitySDK(context); await sdk.initialize(); console.log('Gravity Bridge: ✅ SDK initialized'); return true; } catch (err) { console.log(`Gravity Bridge: SDK init failed: ${err.message}`); return false; } } // Track last seen step per session to avoid re-fetching const lastSeenStep = new Map(); function setupMonitor() { if (!sdk) { return; } // Step count changed → fetch new steps via GetCascadeTrajectorySteps sdk.monitor.onStepCountChanged(async (e) => { console.log(`Gravity Bridge: [SDK] step changed: "${e.title}" step ${e.newCount} (+${e.delta})`); try { // Use the correct LS RPC: GetCascadeTrajectorySteps (not GetConversation which doesn't exist) const fromStep = lastSeenStep.get(e.sessionId) ?? Math.max(0, e.newCount - e.delta); const stepsData = await sdk.ls.rawRPC('GetCascadeTrajectorySteps', { cascadeId: e.sessionId, startStepIndex: fromStep }); lastSeenStep.set(e.sessionId, e.newCount); if (stepsData) { // Try to extract AI text from the steps response const aiText = extractAIText(stepsData); if (aiText) { const text = `🤖 **${e.title}**\n\n${aiText}`; writeChatSnapshot(text); console.log(`Gravity Bridge: [SDK] relayed AI response (${aiText.length} chars)`); return; } // Log the raw structure for debugging console.log(`Gravity Bridge: [SDK] steps data keys: ${JSON.stringify(Object.keys(stepsData))}`); } } catch (err) { console.log(`Gravity Bridge: [SDK] GetCascadeTrajectorySteps error: ${err.message}`); // Fallback: try GetCascadeTrajectory (full trajectory, heavier) try { const fullTraj = await sdk.ls.rawRPC('GetCascadeTrajectory', { cascadeId: e.sessionId }); if (fullTraj) { const aiText = extractAIText(fullTraj); if (aiText) { writeChatSnapshot(`🤖 **${e.title}**\n\n${aiText}`); console.log(`Gravity Bridge: [SDK] relayed via GetCascadeTrajectory (${aiText.length} chars)`); return; } console.log(`Gravity Bridge: [SDK] trajectory keys: ${JSON.stringify(Object.keys(fullTraj))}`); } } catch (err2) { console.log(`Gravity Bridge: [SDK] GetCascadeTrajectory also failed: ${err2.message}`); } } // Fallback: just send the title + step info lastSeenStep.set(e.sessionId, e.newCount); writeChatSnapshot(`🤖 **${e.title}**\n\n(step ${e.newCount}, +${e.delta})`); }); // New conversation started sdk.monitor.onNewConversation(() => { console.log('Gravity Bridge: [SDK] new conversation detected'); }); // Active session changed sdk.monitor.onActiveSessionChanged((e) => { console.log(`Gravity Bridge: [SDK] active session: "${e.title}" (${e.sessionId?.substring(0, 8)})`); }); // State changed (USS update) sdk.monitor.onStateChanged((e) => { console.log(`Gravity Bridge: [SDK] state changed: ${e.key}`); }); // Start monitoring (USS every 3s, trajectory every 2s for faster detection) sdk.monitor.start(3000, 2000); console.log('Gravity Bridge: [SDK] monitor started (USS 3s, trajectory 2s)'); } /** * Extract AI response text from LS RPC step/trajectory data. * The exact structure depends on the protobuf schema — we try multiple paths. */ function extractAIText(data) { if (!data) { return null; } // Try common protobuf response patterns // Pattern 1: steps array with content const steps = data.steps || data.trajectorySteps || data.cascadeSteps; if (Array.isArray(steps) && steps.length > 0) { // Find the last step with AI content for (let i = steps.length - 1; i >= 0; i--) { const step = steps[i]; // PlannerResponse / assistant content const content = step.content || step.text || step.summary || step.plannerResponse || step.assistantMessage || step.response?.content || step.response?.text; if (typeof content === 'string' && content.length > 10) { return content; } } } // Pattern 2: messages array const messages = data.messages || data.chatMessages; if (Array.isArray(messages) && messages.length > 0) { for (let i = messages.length - 1; i >= 0; i--) { const msg = messages[i]; if ((msg.role === 'assistant' || msg.type === 'assistant') && msg.content) { return msg.content; } } } // Pattern 3: nested trajectory object if (data.trajectory) { return extractAIText(data.trajectory); } // Pattern 4: single step response if (data.content && typeof data.content === 'string') { return data.content; } return null; } // ─── Activation ─── async function activate(context) { console.log('Gravity Bridge: activating...'); // Project detection projectName = detectProjectName(); console.log(`Gravity Bridge: project "${projectName}"`); // Bridge path const config = vscode.workspace.getConfiguration('gravityBridge'); const configPath = config.get('bridgePath'); bridgePath = configPath || path.join(os.homedir(), '.gemini', 'antigravity', 'bridge'); ensureBridgeDir(); console.log(`Gravity Bridge: bridge path: ${bridgePath}`); // Status bar statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); statusBar.text = '$(sync~spin) Bridge'; statusBar.tooltip = `Gravity Bridge: ${projectName}`; statusBar.show(); context.subscriptions.push(statusBar); // Initialize SDK const sdkReady = await initSDK(context); if (sdkReady) { setupMonitor(); statusBar.text = '$(check) Bridge SDK'; statusBar.tooltip = `Gravity Bridge: ${projectName} (SDK active)`; // Register SDK-powered commands context.subscriptions.push(vscode.commands.registerCommand('gravityBridge.approve', async () => { try { await sdk.cascade.acceptStep(); vscode.window.showInformationMessage('Gravity Bridge: Step approved'); } catch (e) { vscode.window.showErrorMessage(`Approve failed: ${e.message}`); } }), vscode.commands.registerCommand('gravityBridge.reject', async () => { try { await sdk.cascade.rejectStep(); vscode.window.showInformationMessage('Gravity Bridge: Step rejected'); } catch (e) { vscode.window.showErrorMessage(`Reject failed: ${e.message}`); } })); } else { statusBar.text = '$(warning) Bridge (no SDK)'; console.log('Gravity Bridge: SDK not available, file-based mode only'); } // Watch commands directory watchCommandsDir(); // Register basic commands context.subscriptions.push(vscode.commands.registerCommand('gravityBridge.start', () => { isActive = true; statusBar.text = sdkReady ? '$(check) Bridge SDK' : '$(sync~spin) Bridge'; vscode.window.showInformationMessage(`Gravity Bridge started for "${projectName}"`); }), vscode.commands.registerCommand('gravityBridge.stop', () => { isActive = false; if (sdk) { sdk.monitor.stop(); } statusBar.text = '$(circle-slash) Bridge OFF'; vscode.window.showInformationMessage('Gravity Bridge stopped'); }), vscode.commands.registerCommand('gravityBridge.connect', async () => { if (!sdk) { vscode.window.showErrorMessage('SDK not initialized'); return; } try { const sessions = await sdk.cascade.getSessions(); const items = sessions.map((s) => ({ label: s.title || 'Untitled', description: `step ${s.stepCount} • ${s.id?.substring(0, 8)}`, sessionId: s.id, })); const pick = await vscode.window.showQuickPick(items, { placeHolder: 'Select a conversation to connect' }); if (pick) { await sdk.cascade.focusSession(pick.sessionId); vscode.window.showInformationMessage(`Connected to: ${pick.label}`); } } catch (e) { vscode.window.showErrorMessage(`Connect failed: ${e.message}`); } })); // Cleanup context.subscriptions.push({ dispose: () => { if (sdk) { sdk.monitor.stop(); sdk.dispose(); } if (watcher) { watcher.close(); } if (commandsWatcher) { commandsWatcher.close(); } } }); console.log('Gravity Bridge: ✅ activated'); isActive = true; } function deactivate() { if (sdk) { try { sdk.monitor.stop(); sdk.dispose(); } catch { } } } //# sourceMappingURL=extension.js.map