Files
gravity_control/extension/out/extension.js
CD e7bc4046a4 fix(bridge): hybrid approval — SDK rawRPC + VS Code commands
Root cause: VS Code commands (acceptAgentStep, terminalCommand.run etc)
return undefined silently but don't actually accept WAITING steps.
LS requires HTTPS + CSRF token for RPC calls.

New approach: Phase 1 tries SDK rawRPC (has CSRF auth) with
HandleCascadeUserInteraction + ResolveOutstandingSteps.
Phase 2 tries all VS Code commands as fallback.
All results logged to bridge/extension.log for debugging.

Also removed stall detection (fundamentally broken — stepCount
keeps incrementing from other tool calls during WAITING).
2026-03-08 09:29:40 +09:00

700 lines
28 KiB
JavaScript

"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"));
// ─── File-based logging (AI can read directly) ───
function logToFile(msg) {
try {
if (!bridgePath)
return;
const logFile = path.join(bridgePath, 'extension.log');
const ts = new Date().toISOString().replace('T', ' ').substring(0, 19);
fs.appendFileSync(logFile, `${ts} ${msg}\n`, 'utf-8');
}
catch { }
}
// 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}`);
}
}
// ─── 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();
const lastSnapshotText = new Map();
const registeredSessions = new Set(); // track which sessions have been registered
/**
* Write a registration file for the Bot to discover session → project mapping.
* Called automatically on first step event per session.
*/
function writeRegistration(sessionId) {
if (registeredSessions.has(sessionId)) {
return;
}
registeredSessions.add(sessionId);
try {
const regDir = path.join(bridgePath, 'register');
if (!fs.existsSync(regDir)) {
fs.mkdirSync(regDir, { recursive: true });
}
const data = {
conversation_id: sessionId,
project_name: projectName,
timestamp: Date.now() / 1000,
};
fs.writeFileSync(path.join(regDir, `${sessionId}.json`), JSON.stringify(data, null, 2), 'utf-8');
console.log(`Gravity Bridge: registered session ${sessionId.substring(0, 8)}${projectName}`);
}
catch (e) {
console.log(`Gravity Bridge: registration write error: ${e.message}`);
}
}
function setupMonitor() {
if (!sdk) {
return;
}
// NOTE: SDK EventMonitor DISABLED to prevent ERR_CONNECTION_REFUSED spam.
// Root cause: EventMonitor polls GetCascadeTrajectorySteps every 2s via rawRPC,
// which has a 775-step hard limit and generates connection errors.
// ALL relay is now handled by the GetAllCascadeTrajectories POLL below.
console.log('Gravity Bridge: SDK monitor DISABLED (using GetAllCascadeTrajectories POLL instead)');
// ══════════════════════════════════════════════════════════════════════
// PRIMARY RELAY: GetAllCascadeTrajectories (THE CORRECT API!)
//
// PROVEN VIA DIRECT RPC TESTING:
// - GetCascadeTrajectorySteps: 775-step hard limit, startStepIndex IGNORED
// - getDiagnostics.lastStepIndex: stale (can lag behind)
// - GetAllCascadeTrajectories:
// stepCount: REAL-TIME (verified 1413→1429 live)
// latestNotifyUserStep: contains FULL notificationContent
// latestTaskBoundaryStep: contains FULL taskName/Status/Summary
// stepIndex on each → perfect for dedup
// ══════════════════════════════════════════════════════════════════════
let pollCount = 0;
let activeSessionId = '';
let activeSessionTitle = '';
let lastKnownStepCount = 0;
let lastNotifyStepIndex = -1;
let lastTaskStepIndex = -1;
setInterval(async () => {
pollCount++;
try {
// Single RPC: GetAllCascadeTrajectories
const allTraj = await sdk.ls.rawRPC('GetAllCascadeTrajectories', {});
if (!allTraj?.trajectorySummaries)
return;
// Find the most recently modified session (or current active)
let bestSession = null;
let bestSessionId = '';
let bestModTime = '';
for (const [sid, data] of Object.entries(allTraj.trajectorySummaries)) {
const modTime = data.lastModifiedTime || '';
if (!bestSession || modTime > bestModTime) {
bestSession = data;
bestSessionId = sid;
bestModTime = modTime;
}
}
if (!bestSession)
return;
const currentCount = bestSession.stepCount || 0;
const currentTitle = (bestSession.summary || 'Untitled').substring(0, 50);
const isRunning = String(bestSession.status || '').includes('RUNNING');
// Session changed?
if (bestSessionId !== activeSessionId) {
activeSessionId = bestSessionId;
activeSessionTitle = currentTitle;
lastKnownStepCount = currentCount;
lastNotifyStepIndex = bestSession.latestNotifyUserStep?.stepIndex ?? -1;
lastTaskStepIndex = bestSession.latestTaskBoundaryStep?.stepIndex ?? -1;
writeRegistration(activeSessionId);
console.log(`Gravity Bridge: [POLL#${pollCount}] session: ${activeSessionId.substring(0, 8)} "${currentTitle}" steps=${currentCount} ${isRunning ? 'RUNNING' : 'idle'}`);
return;
}
// No change?
if (currentCount <= lastKnownStepCount && pollCount > 1) {
if (pollCount % 30 === 0) {
console.log(`Gravity Bridge: [POLL#${pollCount}] idle steps=${currentCount}`);
}
// Still process notify/task below (stepIndex may update without stepCount change)
}
const delta = currentCount - lastKnownStepCount;
lastKnownStepCount = currentCount;
if (delta > 0) {
console.log(`Gravity Bridge: [POLL#${pollCount}] +${delta} steps (${currentCount}) "${currentTitle}"`);
}
// ── Process latestNotifyUserStep ──
const notifyStep = bestSession.latestNotifyUserStep;
if (notifyStep && notifyStep.stepIndex > lastNotifyStepIndex) {
lastNotifyStepIndex = notifyStep.stepIndex;
const content = notifyStep.step?.notifyUser?.notificationContent || '';
if (content.length > 10) {
writeChatSnapshot(`📣 **알림** (step ${notifyStep.stepIndex})\n\n${content}`);
console.log(`Gravity Bridge: [POLL#${pollCount}] NOTIFY step=${notifyStep.stepIndex} ${content.length} chars`);
}
}
// ── Process latestTaskBoundaryStep ──
const taskStep = bestSession.latestTaskBoundaryStep;
if (taskStep && taskStep.stepIndex > lastTaskStepIndex) {
lastTaskStepIndex = taskStep.stepIndex;
const tb = taskStep.step?.taskBoundary;
if (tb?.taskName) {
const mode = tb.mode ? tb.mode.replace('AGENT_MODE_', '') : '';
writeChatSnapshot(`📋 **[${mode}] ${tb.taskName}**\n${tb.taskStatus || ''}\n\n${tb.taskSummary || ''}`);
console.log(`Gravity Bridge: [POLL#${pollCount}] TASK step=${taskStep.stepIndex} "${tb.taskName}"`);
}
}
}
catch (e) {
if (pollCount <= 5 || pollCount % 20 === 0) {
console.log(`Gravity Bridge: [POLL#${pollCount}] error: ${e.message}`);
}
}
}, 5000);
}
// ─── Response Watcher (Discord approval → Antigravity RPC) ───
let responseWatcher = null;
function setupResponseWatcher() {
const responseDir = path.join(bridgePath, 'response');
if (!fs.existsSync(responseDir)) {
fs.mkdirSync(responseDir, { recursive: true });
}
try {
responseWatcher = fs.watch(responseDir, (event, filename) => {
if (filename && filename.endsWith('.json') && event === 'rename') {
const fp = path.join(responseDir, filename);
if (fs.existsSync(fp)) {
setTimeout(() => processResponseFile(fp), 300);
}
}
});
console.log('Gravity Bridge: response watcher started');
}
catch (e) {
console.log(`Gravity Bridge: response watcher failed: ${e.message}`);
}
}
async function processResponseFile(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf-8');
const resp = JSON.parse(content);
const msg = `[RESPONSE] rid=${resp.request_id} approved=${resp.approved}`;
console.log(`Gravity Bridge: ${msg}`);
logToFile(msg);
// Find matching pending request for session_id
const pendingDir = path.join(bridgePath, 'pending');
const pendingFile = path.join(pendingDir, `${resp.request_id}.json`);
let sessionId = '';
if (fs.existsSync(pendingFile)) {
try {
const pending = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
sessionId = pending.conversation_id || '';
}
catch { }
}
if (resp.approved) {
// STRATEGY: Try SDK rawRPC first (has CSRF auth), then VS Code commands
// Phase 1: SDK rawRPC (requires active SDK connection with CSRF)
if (sdk && sessionId) {
const rpcMethods = [
{ method: 'HandleCascadeUserInteraction', params: { cascadeId: sessionId, approved: true } },
{ method: 'ResolveOutstandingSteps', params: { cascadeId: sessionId } },
];
for (const { method, params } of rpcMethods) {
try {
const result = await sdk.ls.rawRPC(method, params);
const log = `[RESPONSE] RPC OK ${method}: ${JSON.stringify(result).substring(0, 100)}`;
logToFile(log);
}
catch (e) {
const log = `[RESPONSE] RPC FAIL ${method}: ${e.message}`;
logToFile(log);
}
}
}
else {
logToFile(`[RESPONSE] SDK unavailable (sdk=${!!sdk}, session=${sessionId})`);
}
// Phase 2: VS Code commands (may or may not work depending on UI focus)
const cmds = [
'antigravity.terminalCommand.run',
'antigravity.terminalCommand.accept',
'antigravity.command.accept',
'antigravity.agent.acceptAgentStep',
];
for (const cmd of cmds) {
try {
await vscode.commands.executeCommand(cmd);
logToFile(`[RESPONSE] CMD OK ${cmd}`);
}
catch (e) {
logToFile(`[RESPONSE] CMD FAIL ${cmd}: ${e.message}`);
}
}
logToFile('[RESPONSE] all approve attempts done');
}
else {
// REJECT
if (sdk && sessionId) {
try {
await sdk.ls.rawRPC('HandleCascadeUserInteraction', { cascadeId: sessionId, approved: false });
logToFile('[RESPONSE] RPC reject OK');
}
catch (e) {
logToFile(`[RESPONSE] RPC reject FAIL: ${e.message}`);
}
}
const cmds = [
'antigravity.terminalCommand.reject',
'antigravity.command.reject',
'antigravity.agent.rejectAgentStep',
];
for (const cmd of cmds) {
try {
await vscode.commands.executeCommand(cmd);
logToFile(`[RESPONSE] CMD OK ${cmd}`);
}
catch (e) {
logToFile(`[RESPONSE] CMD FAIL ${cmd}: ${e.message}`);
}
}
logToFile('[RESPONSE] all reject attempts done');
}
try {
fs.unlinkSync(filePath);
}
catch { }
}
catch (e) {
const log = `[RESPONSE] error: ${e.message}`;
console.log(`Gravity Bridge: ${log}`);
logToFile(log);
}
}
/**
* Extract AI text from a PLANNER_RESPONSE step.
* Known structure: {type, status, metadata, plannerResponse, ephemeralMessage, ...}
* ephemeralMessage = system prompt (SKIP), plannerResponse = AI content
*/
function extractPlannerText(step) {
if (!step) {
return null;
}
// Fields to SKIP — not user-facing content
const SKIP_FIELDS = new Set([
'thinking', 'thinkingSignature', 'stopReason', 'type', 'status', 'metadata',
'ephemeralMessage', 'generatorModel', 'requestedModel',
'executionId', 'sourceTrajectoryStepInfo', 'stepIndex',
'viewableAt', 'createdAt', 'finishedGeneratingAt',
'lastCompletedChunkAt', 'source', 'stepGenerationVersion'
]);
// plannerResponse can be string or object
const pr = step.plannerResponse;
if (typeof pr === 'string' && pr.length > 10) {
return filterEphemeral(pr);
}
if (pr && typeof pr === 'object') {
// Try known content fields first (NOT thinking/stopReason)
const text = pr.content || pr.text || pr.summary || pr.message || pr.response || pr.output;
if (typeof text === 'string' && text.length > 10) {
return filterEphemeral(text);
}
// Search other fields, but skip non-content ones
for (const key of Object.keys(pr)) {
if (SKIP_FIELDS.has(key))
continue;
const val = pr[key];
if (typeof val === 'string' && val.length > 50) { // Higher threshold
const filtered = filterEphemeral(val);
if (filtered) {
console.log(`Gravity Bridge: [DEBUG] planner text in plannerResponse.${key} (${filtered.length} chars)`);
return filtered;
}
}
}
}
// Try other step fields (skip known non-content)
for (const key of Object.keys(step)) {
if (SKIP_FIELDS.has(key) || key === 'plannerResponse')
continue;
const val = step[key];
if (typeof val === 'string' && val.length > 50) {
const filtered = filterEphemeral(val);
if (filtered) {
console.log(`Gravity Bridge: [DEBUG] planner text in step.${key}`);
return filtered;
}
}
}
return null;
}
/** Filter out system ephemeral messages and non-content strings. */
function filterEphemeral(text) {
if (!text || text.length < 10) {
return null;
}
// Skip system prompt metadata
if (text.includes('<EPHEMERAL_MESSAGE>') || text.includes('<ephemeral_message>')) {
return null;
}
if (text.includes('artifact_reminder') || text.includes('active_task_reminder')) {
return null;
}
if (text.includes('no_active_task_reminder')) {
return null;
}
// Skip base64/crypto strings (no spaces, mostly alphanumeric)
if (!text.includes(' ') && /^[A-Za-z0-9+/=_\-]{50,}$/.test(text)) {
return null;
}
return text;
}
/** Write a pending approval file matching Bot's ApprovalRequest dataclass. */
function writePendingApproval(data) {
try {
const pendingDir = path.join(bridgePath, 'pending');
if (!fs.existsSync(pendingDir)) {
fs.mkdirSync(pendingDir, { recursive: true });
}
const id = Date.now().toString();
const payload = {
request_id: id,
conversation_id: data.conversation_id,
command: data.command,
description: data.description,
timestamp: Date.now() / 1000,
status: 'pending',
discord_message_id: 0,
project_name: projectName,
};
fs.writeFileSync(path.join(pendingDir, `${id}.json`), JSON.stringify(payload, null, 2), 'utf-8');
console.log(`Gravity Bridge: pending approval written → ${id}.json`);
}
catch (e) {
console.log(`Gravity Bridge: pending write error: ${e.message}`);
}
}
// ─── 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(); // Now just logs that monitor is disabled
statusBar.text = '$(check) Bridge';
statusBar.tooltip = `Gravity Bridge: ${projectName} (POLL 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();
// Watch response directory for approval interactions
setupResponseWatcher();
// 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;
// SDK monitor is disabled, no need to 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) {
try {
sdk.dispose();
}
catch { }
}
if (watcher) {
watcher.close();
}
if (commandsWatcher) {
commandsWatcher.close();
}
}
});
console.log('Gravity Bridge: ✅ activated');
isActive = true;
}
function deactivate() {
if (sdk) {
try {
sdk.dispose();
}
catch { }
}
}
//# sourceMappingURL=extension.js.map