Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ARTIFACT_REGISTRY_FIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,5 @@ gcloud artifacts repositories add-iam-policy-binding gcr.io --location=us --memb





2 changes: 2 additions & 0 deletions STIFF_LAYOUT_SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ In unweighted graphs, geometric patterns (hexagons, triangles, grids) emerge whe





15 changes: 8 additions & 7 deletions agent-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,31 @@
* Replaces the old 5000+ line bridge-daemon-legacy.js with a clean implementation.
*/

process.env.AGENT_SERVER_MODE = 'true';

Check failure on line 10 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined

Check failure on line 10 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined

// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'agent-server.js:TOP',message:'agent-server.js executing',data:{pid:process.pid},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'A'})}).catch(()=>{});
// #endregion

// Import and start the clean wizard server
import { startWizardServer } from './wizard-server.js';
import { debugLogSync } from './src/utils/debugLogger.js';

// #region agent log
debugLogSync('agent-server.js:TOP', 'agent-server.js executing', { pid: process.pid }, 'debug-session', 'A');

Check failure on line 17 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined

Check failure on line 17 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined
// #endregion

// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'agent-server.js:IMPORT_OK',message:'wizard-server imported successfully',data:{},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'B'})}).catch(()=>{});
debugLogSync('agent-server.js:IMPORT_OK', 'wizard-server imported successfully', {}, 'debug-session', 'B');
// #endregion

startWizardServer()
.then(({ port }) => {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'agent-server.js:START_OK',message:'Wizard started successfully',data:{port},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'C'})}).catch(()=>{});
debugLogSync('agent-server.js:START_OK', 'Wizard started successfully', { port }, 'debug-session', 'C');
// #endregion
console.log(`[AgentServer] Wizard service running on port ${port}`);
})
.catch(e => {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'agent-server.js:START_FAIL',message:'Wizard failed to start',data:{error:e.message,stack:e.stack},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'C'})}).catch(()=>{});
debugLogSync('agent-server.js:START_FAIL', 'Wizard failed to start', { error: e.message, stack: e.stack }, 'debug-session', 'C');
// #endregion
console.error('[AgentServer] Failed to start wizard service:', e);
process.exit(1);

Check failure on line 36 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined

Check failure on line 36 in agent-server.js

View workflow job for this annotation

GitHub Actions / test

'process' is not defined
});
5 changes: 3 additions & 2 deletions bridge-daemon-legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cors from 'cors';
import { exec } from 'node:child_process';
import fetch from 'node-fetch';
import queueManager from './src/services/queue/Queue.js';
import { debugLogSync } from './src/utils/debugLogger.js';
import eventLog from './src/services/EventLog.js';
import committer from './src/services/Committer.js';
import { setBridgeStoreRef } from './src/services/bridgeStoreAccessor.js';
Expand Down Expand Up @@ -75,7 +76,7 @@ app.use(express.json({ limit: '2mb' }));

// Serve static files from public directory (for debug viewer)
app.use((req, res, next) => {
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'bridge-daemon-legacy.js:middleware',message:'Incoming request',data:{url: req.url, method: req.method},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'B'})}).catch(()=>{});
debugLogSync('bridge-daemon-legacy.js:middleware', 'Incoming request', { url: req.url, method: req.method }, 'debug-session', 'B');
next();
});
app.use(express.static('public'));
Expand Down Expand Up @@ -4558,7 +4559,7 @@ const startBridgeListener = () => {
const { server: netServer, protocol } = createBridgeServer();
serverProtocol = protocol;
netServer.listen(PORT, () => {
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'bridge-daemon-legacy.js:startBridgeListener',message:'Server started',data:{port: PORT, protocol},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'A'})}).catch(()=>{});
debugLogSync('bridge-daemon-legacy.js:startBridgeListener', 'Server started', { port: PORT, protocol }, 'debug-session', 'A');
console.log(`✅ Bridge daemon listening on ${protocol}://localhost:${PORT}`);
committer.start();
import('./src/services/orchestrator/Scheduler.js').then(mod => { scheduler = mod.default; }).catch(() => { });
Expand Down
2 changes: 2 additions & 0 deletions electron-builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@





2 changes: 2 additions & 0 deletions scripts/fix-artifact-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,5 @@ echo -e "${BLUE}You can now run your deployment scripts.${NC}"





5 changes: 3 additions & 2 deletions src/NodeCanvas.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ForceSimulationModal from './components/ForceSimulationModal';
import { parseInputData, generateGraph } from './services/autoGraphGenerator';
import { applyLayout, getClusterGeometries, FORCE_LAYOUT_DEFAULTS } from './services/graphLayoutService.js';
import { NavigationMode, calculateNavigationParams } from './services/canvasNavigationService.js';
import { debugLogSync } from './utils/debugLogger.js';

// Import Zustand store and selectors/actions
import useGraphStore, {
Expand Down Expand Up @@ -5308,7 +5309,7 @@ function NodeCanvas() {

const handleWheel = async (e) => {
// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'NodeCanvas.jsx:5282', message: 'handleWheel START', data: { deltaY: e.deltaY?.toFixed?.(2), ctrlKey: e.ctrlKey }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'C' }) }).catch(() => { });
debugLogSync('NodeCanvas.jsx:handleWheel', 'handleWheel START', { deltaY: e.deltaY?.toFixed?.(2), ctrlKey: e.ctrlKey }, 'debug-session', 'C');
// #endregion
// If a gesture/pinch is active, ignore wheel to prevent double-handling on Safari
if (pinchRef.current.active) {
Expand Down Expand Up @@ -9944,7 +9945,7 @@ function NodeCanvas() {
// #region agent log
const multiEdgePairs = Array.from(edgePairGroups.entries()).filter(([k, v]) => v.length > 1);
if (multiEdgePairs.length > 0) {
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'NodeCanvas.jsx:edgeRender', message: 'Edge rendering info', data: { totalEdges: visibleEdges.length, multiEdgePairs: multiEdgePairs.map(([k, v]) => ({ pair: k, edgeCount: v.length, edgeIds: v })), enableAutoRouting, routingStyle, willUseCurves: !(enableAutoRouting && (routingStyle === 'manhattan' || routingStyle === 'clean')) }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'D-E' }) }).catch(() => { });
debugLogSync('NodeCanvas.jsx:edgeRender', 'Edge rendering info', { totalEdges: visibleEdges.length, multiEdgePairs: multiEdgePairs.map(([k, v]) => ({ pair: k, edgeCount: v.length, edgeIds: v })), enableAutoRouting, routingStyle, willUseCurves: !(enableAutoRouting && (routingStyle === 'manhattan' || routingStyle === 'clean')) }, 'debug-session', 'D-E');
}
// #endregion

Expand Down
5 changes: 3 additions & 2 deletions src/components/panel/views/LeftAIView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ const LeftAIView = ({ compact = false, activeGraphId, graphsMap, edgesMap }) =>
}
});

eventSource.onerror = (err) => {
console.warn('[AI Collaboration] SSE error:', err);
eventSource.onerror = () => {
// Silently handle SSE errors - server may not be available
// Connection errors are expected when bridge server isn't running
};
} catch (err) {
console.warn('[AI Collaboration] Failed to establish SSE:', err);
Expand Down
2 changes: 2 additions & 0 deletions src/services/agentRuntime/Executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,5 @@ export function execute(plan, context, cid, ensureSchedulerStarted) {





2 changes: 2 additions & 0 deletions src/services/agentRuntime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,5 @@ const result = await coordinator.handle({





2 changes: 2 additions & 0 deletions src/services/agentRuntime/StateMirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,5 @@ export function createInitialState() {





122 changes: 119 additions & 3 deletions src/services/bridgeConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,127 @@ export function bridgeFetch(path, options) {

export function bridgeEventSource(path) {
// Consumers should pass a path like '/events/stream'
return new EventSource(bridgeUrl(path));
const eventSource = new EventSource(bridgeUrl(path));

// Add error handler to suppress console errors when server is not available
eventSource.addEventListener('error', (event) => {
// Silently handle connection errors - don't log to console
// The error event will still fire, but we prevent console spam
if (eventSource.readyState === EventSource.CLOSED) {
// Connection closed - server likely not available
// Silently close and don't log
try {
eventSource.close();
} catch (e) {
// Ignore errors during close
}
}
}, { once: false });

return eventSource;
}

// OAuth server availability cache
// Try to restore from sessionStorage to persist across page reloads
function loadOAuthHealth() {
try {
if (typeof sessionStorage !== 'undefined') {
const stored = sessionStorage.getItem('redstring_oauth_health');
if (stored) {
const parsed = JSON.parse(stored);
const now = Date.now();
// Only use stored data if cooldown is still active
if (parsed.cooldownUntil > now) {
return {
consecutiveFailures: parsed.consecutiveFailures || 0,
cooldownUntil: parsed.cooldownUntil,
isAvailable: false,
lastChecked: parsed.lastChecked || 0,
firstFailureTime: parsed.firstFailureTime || 0
};
}
}
}
} catch (e) {
// Ignore storage errors
}
return {
consecutiveFailures: 0,
cooldownUntil: 0,
isAvailable: true,
lastChecked: 0,
firstFailureTime: 0
};
}

function saveOAuthHealth(health) {
try {
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('redstring_oauth_health', JSON.stringify({
consecutiveFailures: health.consecutiveFailures,
cooldownUntil: health.cooldownUntil,
lastChecked: health.lastChecked,
firstFailureTime: health.firstFailureTime
}));
}
} catch (e) {
// Ignore storage errors
}
}

// OAuth-specific fetch function (separate server, no circuit breaker needed)
const __oauthHealth = loadOAuthHealth();

// OAuth-specific fetch function with aggressive error suppression
export function oauthFetch(path, options) {
return fetch(oauthUrl(path), options);
const now = Date.now();

// If in cooldown period, don't make the request at all (prevents browser console errors)
if (__oauthHealth.cooldownUntil > now) {
// Return a rejected promise that won't trigger network request
return Promise.reject(new Error('OAuth server unavailable (cooldown)'));
}

// Make the request with a timeout to fail fast
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
const timeoutId = controller ? setTimeout(() => controller.abort(), 2000) : null; // 2 second timeout

const fetchOptions = {
...options,
...(controller ? { signal: controller.signal } : {})
};

return fetch(oauthUrl(path), fetchOptions)
.then((res) => {
if (timeoutId) clearTimeout(timeoutId);
// Reset failures on any response (even errors)
__oauthHealth.consecutiveFailures = 0;
__oauthHealth.isAvailable = true;
__oauthHealth.lastChecked = now;
__oauthHealth.firstFailureTime = 0;
__oauthHealth.cooldownUntil = 0;
saveOAuthHealth(__oauthHealth);
return res;
})
.catch((err) => {
if (timeoutId) clearTimeout(timeoutId);
__oauthHealth.lastChecked = now;

if (isLikelyNetworkRefusal(err) || err.name === 'AbortError') {
__oauthHealth.consecutiveFailures += 1;
__oauthHealth.isAvailable = false;

// Set cooldown immediately after first failure (prevents spam)
if (__oauthHealth.consecutiveFailures === 1) {
__oauthHealth.firstFailureTime = now;
__oauthHealth.cooldownUntil = now + 300000; // 5 minute cooldown after first failure
} else if (__oauthHealth.consecutiveFailures > 1) {
// Extend cooldown with each failure
__oauthHealth.cooldownUntil = now + Math.min(300000, 60000 * __oauthHealth.consecutiveFailures);
}
saveOAuthHealth(__oauthHealth);
}
// Re-throw but the caller should handle it gracefully
throw err;
});
}

9 changes: 5 additions & 4 deletions src/services/orchestrator/roleRunners.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RolePrompts, ToolAllowlists } from '../roles.js';
import { getBridgeStore, getGraphById, getActiveGraph } from '../bridgeStoreAccessor.js';
import { getGraphSemanticStructure } from '../graphQueries.js';
import executionTracer from '../ExecutionTracer.js';
import { debugLogSync } from '../../utils/debugLogger.js';

// Helper to normalize string to Title Case
function toTitleCase(str) {
Expand Down Expand Up @@ -196,13 +197,13 @@ export async function runExecutorOnce() {
if (!allow.has(task.toolName)) throw new Error(`Tool not allowed for executor: ${task.toolName}`);
// #region agent log
if (task.toolName === 'create_group') {
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'roleRunners.js:pre-validation', message: 'About to validate create_group', data: { toolName: task.toolName, argsKeys: Object.keys(task.args || {}) }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'H3' }) }).catch(() => { });
debugLogSync('roleRunners.js:pre-validation', 'About to validate create_group', { toolName: task.toolName, argsKeys: Object.keys(task.args || {}) }, 'debug-session', 'H3');
}
// #endregion
const validation = toolValidator.validateToolArgs(task.toolName, task.args || {});
// #region agent log
if (task.toolName === 'create_group') {
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'roleRunners.js:post-validation', message: 'Validation result for create_group', data: { valid: validation.valid, error: validation.error, sanitizedKeys: validation.sanitized ? Object.keys(validation.sanitized) : null }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'H3' }) }).catch(() => { });
debugLogSync('roleRunners.js:post-validation', 'Validation result for create_group', { valid: validation.valid, error: validation.error, sanitizedKeys: validation.sanitized ? Object.keys(validation.sanitized) : null }, 'debug-session', 'H3');
}
// #endregion
if (!validation.valid) {
Expand Down Expand Up @@ -708,7 +709,7 @@ export async function runExecutorOnce() {
const isExpandMode = providedGraphId && !name;
const graphId = providedGraphId || `graph-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'roleRunners.js:create_populated_graph', message: 'Mode determined', data: { isExpandMode, hasName: !!name, hasGraphId: !!providedGraphId, graphId }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'fix-verify', runId: 'post-fix' }) }).catch(() => { });
debugLogSync('roleRunners.js:create_populated_graph', 'Mode determined', { isExpandMode, hasName: !!name, hasGraphId: !!providedGraphId, graphId }, 'debug-session', 'fix-verify');
// #endregion

// 1. Create the graph (only if not in expand mode)
Expand Down Expand Up @@ -1545,7 +1546,7 @@ export async function runExecutorOnce() {
}

// #region agent log
fetch('http://127.0.0.1:7242/ingest/52d0fe28-158e-49a4-b331-f013fcb14181', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ location: 'roleRunners.js:create_edge', message: 'Executor creating edge op', data: { edgeId, sourceInstanceId, targetInstanceId, graphId, name, typeNodeId }, timestamp: Date.now(), sessionId: 'debug-session', hypothesisId: 'A-C' }) }).catch(() => { });
debugLogSync('roleRunners.js:create_edge', 'Executor creating edge op', { edgeId, sourceInstanceId, targetInstanceId, graphId, name, typeNodeId }, 'debug-session', 'A-C');
// #endregion
ops.push({
type: 'addEdge',
Expand Down
9 changes: 5 additions & 4 deletions src/services/persistentAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ export class PersistentAuth {
// OPTIONAL: Sync from server as backup (stateless server doesn't persist)
// This is only useful for initial server-side OAuth completion
try {
const response = await oauthFetch('/api/github/auth/state?includeTokens=true');
if (response.ok) {
const response = await oauthFetch('/api/github/auth/state?includeTokens=true').catch(() => null);
if (response && response.ok) {
const state = await response.json();
// Only apply server state if browser doesn't have tokens
if (!this.oauthCache?.accessToken && state?.oauth?.accessToken) {
Expand All @@ -167,7 +167,8 @@ export class PersistentAuth {
}
}
} catch (serverError) {
console.log('[PersistentAuth] Server sync skipped (expected for stateless server):', serverError.message);
// Silently handle - OAuth server may not be running
// Browser will log network errors, but we don't add additional logging
}

this.authStateLoaded = true;
Expand Down Expand Up @@ -463,7 +464,7 @@ export class PersistentAuth {
console.log('[PersistentAuth] No stored GitHub App installation found; attempting discovery...');
try {
// Ask backend for installations associated with this app
const listResp = await oauthFetch('/api/github/app/installations');
const listResp = await oauthFetch('/api/github/app/installations').catch(() => null);
if (listResp && listResp.ok) {
const installations = await listResp.json();
if (Array.isArray(installations) && installations.length > 0) {
Expand Down
2 changes: 2 additions & 0 deletions src/services/save.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ self.onmessage = (e) => {





Loading
Loading