aegiscode 3.1.10 ā 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aegis.js +165 -0
- package/package.json +18 -86
- package/LICENSE +0 -21
- package/README.md +0 -394
- package/dist/main.js +0 -2171
- package/gui/run-gui.cjs +0 -13
- package/src/agent/Agent.ts +0 -903
- package/src/agent/SimpleAgent.ts +0 -48
- package/src/agent/index.ts +0 -54
- package/src/agent/orchestrator/AppBuilder.ts +0 -443
- package/src/agent/orchestrator/CouncilAgent.ts +0 -310
- package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
- package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
- package/src/agent/orchestrator/index.ts +0 -38
- package/src/agent/orchestrator/utils.ts +0 -397
- package/src/agent/pricing.ts +0 -115
- package/src/agent/router.ts +0 -74
- package/src/agent/routerStats.ts +0 -121
- package/src/agent/types.ts +0 -318
- package/src/auth/login.ts +0 -383
- package/src/cli/config.ts +0 -189
- package/src/cli/index.ts +0 -17
- package/src/cli/middleware.ts +0 -119
- package/src/cli/types.ts +0 -75
- package/src/config/ConfigManager.ts +0 -587
- package/src/config/index.ts +0 -7
- package/src/config/types.ts +0 -584
- package/src/context/CompactionService.ts +0 -300
- package/src/context/ContextManager.ts +0 -450
- package/src/context/FileAnalyzer.ts +0 -267
- package/src/context/TokenCounter.ts +0 -265
- package/src/context/index.ts +0 -27
- package/src/context/storage/CacheStore.ts +0 -176
- package/src/context/storage/JSONLStore.ts +0 -201
- package/src/context/storage/MemoryStore.ts +0 -205
- package/src/context/storage/PersistentStore.ts +0 -327
- package/src/context/storage/index.ts +0 -9
- package/src/context/storage/pathUtils.ts +0 -114
- package/src/context/test.ts +0 -309
- package/src/context/types.ts +0 -268
- package/src/hooks/HookExecutor.ts +0 -434
- package/src/hooks/HookManager.ts +0 -596
- package/src/hooks/HookService.ts +0 -269
- package/src/hooks/Matcher.ts +0 -157
- package/src/hooks/index.ts +0 -63
- package/src/hooks/types.ts +0 -424
- package/src/main.tsx +0 -596
- package/src/mcp/HealthMonitor.ts +0 -150
- package/src/mcp/McpClient.ts +0 -491
- package/src/mcp/McpRegistry.ts +0 -321
- package/src/mcp/createMcpTool.ts +0 -251
- package/src/mcp/index.ts +0 -15
- package/src/mcp/server.ts +0 -334
- package/src/mcp/test-server.ts +0 -88
- package/src/mcp/test.ts +0 -372
- package/src/mcp/types.ts +0 -247
- package/src/memory/AgentMemoryBus.ts +0 -432
- package/src/memory/CloudSync.ts +0 -99
- package/src/memory/DriveSync.ts +0 -106
- package/src/memory/SharedMemory.ts +0 -951
- package/src/memory/index.ts +0 -14
- package/src/memory/machineFingerprint.ts +0 -40
- package/src/orchestrator/SubAgentMetadata.ts +0 -136
- package/src/prompts/builder.ts +0 -213
- package/src/prompts/default.ts +0 -144
- package/src/prompts/index.ts +0 -16
- package/src/prompts/plan.ts +0 -64
- package/src/prompts/test.ts +0 -78
- package/src/services/AnthropicChatService.ts +0 -341
- package/src/services/ChatService.ts +0 -347
- package/src/services/ClaudeCliChatService.ts +0 -256
- package/src/services/CloudSync.ts +0 -168
- package/src/services/CostLedger.ts +0 -211
- package/src/services/Heartbeat.ts +0 -135
- package/src/services/LearningCollector.ts +0 -291
- package/src/services/OllamaInstaller.ts +0 -342
- package/src/services/VersionChecker.ts +0 -445
- package/src/services/index.ts +0 -57
- package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
- package/src/services/streaming/RenderingProfile.ts +0 -90
- package/src/services/streaming/StreamEventParser.ts +0 -181
- package/src/services/streaming/ThrottledRenderer.ts +0 -139
- package/src/services/streaming/TranscriptBuffer.ts +0 -574
- package/src/services/streaming/eventStatusMap.ts +0 -52
- package/src/services/streaming/index.ts +0 -46
- package/src/services/streaming/renderFormatting.ts +0 -79
- package/src/services/streaming/types.ts +0 -234
- package/src/skills/SkillLoader.ts +0 -126
- package/src/skills/SkillRegistry.ts +0 -366
- package/src/skills/index.ts +0 -48
- package/src/skills/types.ts +0 -146
- package/src/slash-commands/billing.ts +0 -70
- package/src/slash-commands/build.ts +0 -413
- package/src/slash-commands/builtinCommands.ts +0 -2733
- package/src/slash-commands/clone.ts +0 -242
- package/src/slash-commands/council.ts +0 -125
- package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
- package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
- package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
- package/src/slash-commands/custom/index.ts +0 -7
- package/src/slash-commands/debate.ts +0 -254
- package/src/slash-commands/gmail.ts +0 -105
- package/src/slash-commands/index.ts +0 -388
- package/src/slash-commands/mcpCommand.ts +0 -205
- package/src/slash-commands/types.ts +0 -201
- package/src/store/index.ts +0 -76
- package/src/store/selectors.ts +0 -246
- package/src/store/slices/appSlice.ts +0 -205
- package/src/store/slices/commandSlice.ts +0 -115
- package/src/store/slices/configSlice.ts +0 -46
- package/src/store/slices/focusSlice.ts +0 -64
- package/src/store/slices/index.ts +0 -9
- package/src/store/slices/sessionSlice.ts +0 -424
- package/src/store/streaming-buffer.ts +0 -425
- package/src/store/test.ts +0 -296
- package/src/store/types.ts +0 -274
- package/src/store/vanilla.ts +0 -186
- package/src/tools/builtin/bash.ts +0 -236
- package/src/tools/builtin/council.ts +0 -105
- package/src/tools/builtin/edit.ts +0 -213
- package/src/tools/builtin/glob.ts +0 -136
- package/src/tools/builtin/grep.ts +0 -263
- package/src/tools/builtin/index.ts +0 -61
- package/src/tools/builtin/memory.ts +0 -66
- package/src/tools/builtin/read.ts +0 -168
- package/src/tools/builtin/skill.ts +0 -97
- package/src/tools/builtin/snapshot.ts +0 -40
- package/src/tools/builtin/task.ts +0 -106
- package/src/tools/builtin/write.ts +0 -134
- package/src/tools/createTool.ts +0 -221
- package/src/tools/execution/ExecutionPipeline.ts +0 -263
- package/src/tools/execution/index.ts +0 -40
- package/src/tools/execution/stages/CacheStage.ts +0 -131
- package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
- package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
- package/src/tools/execution/stages/ExecutionStage.ts +0 -48
- package/src/tools/execution/stages/FormattingStage.ts +0 -44
- package/src/tools/execution/stages/HookStage.ts +0 -72
- package/src/tools/execution/stages/PermissionStage.ts +0 -287
- package/src/tools/execution/stages/PostHookStage.ts +0 -71
- package/src/tools/execution/stages/index.ts +0 -12
- package/src/tools/execution/test.ts +0 -266
- package/src/tools/execution/types.ts +0 -273
- package/src/tools/index.ts +0 -81
- package/src/tools/registry.ts +0 -304
- package/src/tools/schemas.ts +0 -109
- package/src/tools/test.ts +0 -220
- package/src/tools/types.ts +0 -175
- package/src/tools/validation/PermissionChecker.ts +0 -242
- package/src/tools/validation/SensitiveFileDetector.ts +0 -210
- package/src/tools/validation/index.ts +0 -11
- package/src/ui/App.tsx +0 -166
- package/src/ui/components/AegisInterface.tsx +0 -484
- package/src/ui/components/common/ChatSearch.tsx +0 -150
- package/src/ui/components/common/ErrorBoundary.tsx +0 -82
- package/src/ui/components/common/ExitMessage.tsx +0 -120
- package/src/ui/components/common/LoadingIndicator.tsx +0 -49
- package/src/ui/components/common/index.ts +0 -6
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
- package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
- package/src/ui/components/dialog/SetupWizard.tsx +0 -297
- package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
- package/src/ui/components/dialog/index.ts +0 -8
- package/src/ui/components/index.ts +0 -28
- package/src/ui/components/input/CommandSuggestions.tsx +0 -139
- package/src/ui/components/input/CustomTextInput.tsx +0 -220
- package/src/ui/components/input/InputArea.tsx +0 -361
- package/src/ui/components/input/PromptSuggestions.tsx +0 -66
- package/src/ui/components/input/index.ts +0 -6
- package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
- package/src/ui/components/layout/ContextBar.tsx +0 -79
- package/src/ui/components/layout/MessageArea.tsx +0 -96
- package/src/ui/components/layout/MessageList.tsx +0 -647
- package/src/ui/components/layout/MessageSeparator.tsx +0 -26
- package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
- package/src/ui/components/layout/index.ts +0 -7
- package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
- package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
- package/src/ui/components/markdown/index.ts +0 -8
- package/src/ui/components/markdown/parser.ts +0 -336
- package/src/ui/components/markdown/types.ts +0 -66
- package/src/ui/focus/FocusManager.ts +0 -137
- package/src/ui/focus/index.ts +0 -13
- package/src/ui/focus/types.ts +0 -54
- package/src/ui/focus/useFocus.ts +0 -75
- package/src/ui/hooks/index.ts +0 -11
- package/src/ui/hooks/useAgent.ts +0 -284
- package/src/ui/hooks/useCommandHistory.ts +0 -87
- package/src/ui/hooks/useCommandProcessor.ts +0 -443
- package/src/ui/hooks/useConfirmation.ts +0 -99
- package/src/ui/hooks/useCtrlCHandler.ts +0 -100
- package/src/ui/hooks/useInputBuffer.ts +0 -122
- package/src/ui/hooks/useTerminalSize.ts +0 -68
- package/src/ui/hooks/useTerminalWidth.ts +0 -5
- package/src/ui/hooks/useWindowedList.ts +0 -118
- package/src/ui/render-debugger.ts +0 -621
- package/src/ui/test.ts +0 -189
- package/src/ui/themes/ThemeManager.ts +0 -332
- package/src/ui/themes/aegisTheme.ts +0 -87
- package/src/ui/themes/darkTheme.ts +0 -87
- package/src/ui/themes/defaultTheme.ts +0 -85
- package/src/ui/themes/index.ts +0 -10
- package/src/ui/themes/lightTheme.ts +0 -89
- package/src/ui/themes/popularThemes.ts +0 -187
- package/src/ui/themes/types.ts +0 -130
- package/src/utils/clipboard.ts +0 -48
- package/src/utils/debug.ts +0 -43
- package/src/utils/environment.ts +0 -68
- package/src/utils/index.ts +0 -10
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AEGISCode Rendering Debugger
|
|
3
|
-
*
|
|
4
|
-
* Injects probes into the rendering pipeline to detect:
|
|
5
|
-
* 1. Re-render storms (>10 renders/sec on any component)
|
|
6
|
-
* 2. Cascading re-renders (parent triggers child unnecessarily)
|
|
7
|
-
* 3. Stale closures / missing deps (state change without re-render)
|
|
8
|
-
* 4. Infinite render loops (render ā state ā render without RAF break)
|
|
9
|
-
* 5. Streaming render latency (time between store flush and DOM update)
|
|
10
|
-
* 6. Memo comparator regressions (skipped re-render when content changed)
|
|
11
|
-
* 7. Zustand subscription leaks (subscriptions not cleaned up)
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* import { startRenderDebugger, stopRenderDebugger } from './render-debugger.js'
|
|
15
|
-
* startRenderDebugger()
|
|
16
|
-
*
|
|
17
|
-
* Or via CLI:
|
|
18
|
-
* aegis --debug-rendering
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
// ============ Types ============
|
|
22
|
-
|
|
23
|
-
interface RenderEvent {
|
|
24
|
-
type: 'mount' | 'update' | 'unmount' | 'state-change' | 'store-sub' | 'raf-poll'
|
|
25
|
-
component: string
|
|
26
|
-
timestamp: number
|
|
27
|
-
duration?: number // ms since last render of same component
|
|
28
|
-
prevProps?: string // JSON-hash of previous props
|
|
29
|
-
nextProps?: string // JSON-hash of current props
|
|
30
|
-
stack?: string // limited call stack
|
|
31
|
-
messageId?: string // for streaming messages
|
|
32
|
-
contentLen?: number // content length delta
|
|
33
|
-
storeAction?: string // which store action triggered this
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface ComponentStats {
|
|
37
|
-
renderCount: number
|
|
38
|
-
lastRender: number
|
|
39
|
-
minInterval: number
|
|
40
|
-
maxInterval: number
|
|
41
|
-
totalTime: number // ms since first render
|
|
42
|
-
warnings: string[]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface DebuggerState {
|
|
46
|
-
enabled: boolean
|
|
47
|
-
events: RenderEvent[]
|
|
48
|
-
componentStats: Map<string, ComponentStats>
|
|
49
|
-
intervalId: ReturnType<typeof setInterval> | null
|
|
50
|
-
originalRaf: typeof requestAnimationFrame
|
|
51
|
-
originalSetState: any
|
|
52
|
-
componentRenderCounts: Map<string, number>
|
|
53
|
-
history: RenderEvent[]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ============ Globals ============
|
|
57
|
-
|
|
58
|
-
const RENDER_STORM_THRESHOLD = 10 // renders/sec
|
|
59
|
-
const CASCADE_THRESHOLD = 50 // ms between parentāchild renders
|
|
60
|
-
const MAX_EVENTS = 1000
|
|
61
|
-
const REPORT_INTERVAL = 3000 // ms between auto-reports
|
|
62
|
-
|
|
63
|
-
const state: DebuggerState = {
|
|
64
|
-
enabled: false,
|
|
65
|
-
events: [],
|
|
66
|
-
componentStats: new Map(),
|
|
67
|
-
intervalId: null,
|
|
68
|
-
originalRaf: globalThis.requestAnimationFrame,
|
|
69
|
-
originalSetState: null,
|
|
70
|
-
componentRenderCounts: new Map(),
|
|
71
|
-
history: [],
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ============ Helpers ============
|
|
75
|
-
|
|
76
|
-
function componentName(element: any): string {
|
|
77
|
-
if (!element) return '<unknown>'
|
|
78
|
-
if (typeof element === 'string') return element
|
|
79
|
-
if (element.displayName) return element.displayName
|
|
80
|
-
if (element.name) return element.name
|
|
81
|
-
if (element.type) {
|
|
82
|
-
if (typeof element.type === 'string') return element.type
|
|
83
|
-
return element.type.displayName || element.type.name || '<anonymous>'
|
|
84
|
-
}
|
|
85
|
-
return '<unknown>'
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function hashProps(props: Record<string, any>): string {
|
|
89
|
-
const keys = Object.keys(props).sort()
|
|
90
|
-
const parts = keys.map(k => {
|
|
91
|
-
const v = props[k]
|
|
92
|
-
if (typeof v === 'function') return `${k}:fn`
|
|
93
|
-
if (typeof v === 'object' && v !== null) return `${k}:obj`
|
|
94
|
-
return `${k}:${String(v).slice(0, 50)}`
|
|
95
|
-
})
|
|
96
|
-
return parts.join('|')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function getCallStack(limit = 3): string {
|
|
100
|
-
const err = new Error()
|
|
101
|
-
const stack = err.stack?.split('\n').slice(3, 3 + limit) || []
|
|
102
|
-
return stack.map(s => s.trim()).join(' ā ')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ============ Component Render Hooks ============
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Patch React.createElement to track component renders.
|
|
109
|
-
* We wrap the original to count renders per component type.
|
|
110
|
-
*/
|
|
111
|
-
function patchReactCreateElement(React: any): () => void {
|
|
112
|
-
if (!React || !React.createElement) return () => {}
|
|
113
|
-
|
|
114
|
-
const originalCreateElement = React.createElement
|
|
115
|
-
|
|
116
|
-
React.createElement = function patchedCreateElement(type: any, props: any, ...children: any[]) {
|
|
117
|
-
if (state.enabled) {
|
|
118
|
-
const name = componentName({ type })
|
|
119
|
-
const count = state.componentRenderCounts.get(name) || 0
|
|
120
|
-
state.componentRenderCounts.set(name, count + 1)
|
|
121
|
-
|
|
122
|
-
const event: RenderEvent = {
|
|
123
|
-
type: count === 0 ? 'mount' : 'update',
|
|
124
|
-
component: name,
|
|
125
|
-
timestamp: performance.now(),
|
|
126
|
-
stack: getCallStack(2),
|
|
127
|
-
nextProps: props ? hashProps(props) : '{}',
|
|
128
|
-
}
|
|
129
|
-
state.events.push(event)
|
|
130
|
-
if (state.events.length > MAX_EVENTS) state.events.shift()
|
|
131
|
-
}
|
|
132
|
-
return originalCreateElement.call(React, type, props, ...children)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return () => {
|
|
136
|
-
React.createElement = originalCreateElement
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ============ Store Subscription Monitor ============
|
|
141
|
-
|
|
142
|
-
let storeUnsubPatched = false
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Patch zustand subscribe to detect subscription leaks
|
|
146
|
-
* and log store action ā render causality.
|
|
147
|
-
*/
|
|
148
|
-
function patchStoreSubscribe(store: any): () => void {
|
|
149
|
-
if (!store || storeUnsubPatched) return () => {}
|
|
150
|
-
storeUnsubPatched = true
|
|
151
|
-
|
|
152
|
-
const origSubscribe = store.subscribe.bind(store)
|
|
153
|
-
|
|
154
|
-
const wrappedSubscribe = (selector: any, callback?: any) => {
|
|
155
|
-
const wrappedCallback = callback
|
|
156
|
-
? (state: any, prevState?: any) => {
|
|
157
|
-
if (state.enabled) {
|
|
158
|
-
state.events.push({
|
|
159
|
-
type: 'store-sub',
|
|
160
|
-
component: 'store',
|
|
161
|
-
timestamp: performance.now(),
|
|
162
|
-
storeAction: 'subscribe-callback fired',
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
callback(state, prevState)
|
|
166
|
-
}
|
|
167
|
-
: selector
|
|
168
|
-
|
|
169
|
-
const unsub = origSubscribe(selector, wrappedCallback)
|
|
170
|
-
return unsub
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
store.subscribe = wrappedSubscribe
|
|
174
|
-
|
|
175
|
-
return () => {
|
|
176
|
-
store.subscribe = origSubscribe
|
|
177
|
-
storeUnsubPatched = false
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// ============ RAF Monitor ============
|
|
182
|
-
|
|
183
|
-
let rafCallCount = 0
|
|
184
|
-
let rafResetTimer: ReturnType<typeof setTimeout> | null = null
|
|
185
|
-
|
|
186
|
-
function patchRAF(): () => void {
|
|
187
|
-
const originalRaf = globalThis.requestAnimationFrame
|
|
188
|
-
const originalCaf = globalThis.cancelAnimationFrame
|
|
189
|
-
|
|
190
|
-
const activeRafs = new Set<number>()
|
|
191
|
-
|
|
192
|
-
;(globalThis as any).requestAnimationFrame = (cb: FrameRequestCallback): number => {
|
|
193
|
-
rafCallCount++
|
|
194
|
-
if (rafResetTimer) clearTimeout(rafResetTimer)
|
|
195
|
-
rafResetTimer = setTimeout(() => { rafCallCount = 0 }, 1000)
|
|
196
|
-
|
|
197
|
-
const wrapped: FrameRequestCallback = (time) => {
|
|
198
|
-
activeRafs.delete(id)
|
|
199
|
-
cb(time)
|
|
200
|
-
}
|
|
201
|
-
const id = originalRaf.call(globalThis, wrapped)
|
|
202
|
-
activeRafs.add(id)
|
|
203
|
-
return id
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
;(globalThis as any).cancelAnimationFrame = (id: number) => {
|
|
207
|
-
activeRafs.delete(id)
|
|
208
|
-
originalCaf.call(globalThis, id)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return () => {
|
|
212
|
-
;(globalThis as any).requestAnimationFrame = originalRaf
|
|
213
|
-
;(globalThis as any).cancelAnimationFrame = originalCaf
|
|
214
|
-
activeRafs.forEach(id => originalCaf.call(globalThis, id))
|
|
215
|
-
activeRafs.clear()
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// ============ Console Monitoring ============
|
|
220
|
-
|
|
221
|
-
function patchConsole(): () => void {
|
|
222
|
-
const origWarn = console.warn
|
|
223
|
-
const origError = console.error
|
|
224
|
-
|
|
225
|
-
;(console as any).warn = (...args: any[]) => {
|
|
226
|
-
const msg = args.join(' ')
|
|
227
|
-
if (
|
|
228
|
-
msg.includes('maximum update depth') ||
|
|
229
|
-
msg.includes('React has detected') ||
|
|
230
|
-
msg.includes('Cannot update during') ||
|
|
231
|
-
msg.includes('render') && msg.includes('state')
|
|
232
|
-
) {
|
|
233
|
-
state.events.push({
|
|
234
|
-
type: 'update',
|
|
235
|
-
component: '[React Warning]',
|
|
236
|
-
timestamp: performance.now(),
|
|
237
|
-
nextProps: msg.slice(0, 200),
|
|
238
|
-
stack: getCallStack(5),
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
return origWarn.apply(console, args)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
;(console as any).error = (...args: any[]) => {
|
|
245
|
-
const msg = args.join(' ')
|
|
246
|
-
if (
|
|
247
|
-
msg.includes('Minified React error') ||
|
|
248
|
-
msg.includes('Rendered more hooks') ||
|
|
249
|
-
msg.includes('Rendered fewer hooks')
|
|
250
|
-
) {
|
|
251
|
-
state.events.push({
|
|
252
|
-
type: 'update',
|
|
253
|
-
component: '[React Error]',
|
|
254
|
-
timestamp: performance.now(),
|
|
255
|
-
nextProps: msg.slice(0, 200),
|
|
256
|
-
stack: getCallStack(5),
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
return origError.apply(console, args)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return () => {
|
|
263
|
-
console.warn = origWarn
|
|
264
|
-
console.error = origError
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// ============ Analysis ============
|
|
269
|
-
|
|
270
|
-
function analyzeRenderEvents(): {
|
|
271
|
-
storms: string[]
|
|
272
|
-
cascades: string[]
|
|
273
|
-
loops: boolean
|
|
274
|
-
streamingIssues: string[]
|
|
275
|
-
summary: Record<string, number>
|
|
276
|
-
} {
|
|
277
|
-
const result = {
|
|
278
|
-
storms: [] as string[],
|
|
279
|
-
cascades: [] as string[],
|
|
280
|
-
loops: false,
|
|
281
|
-
streamingIssues: [] as string[],
|
|
282
|
-
summary: {} as Record<string, number>,
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const perComponent = new Map<string, RenderEvent[]>()
|
|
286
|
-
for (const ev of state.events) {
|
|
287
|
-
if (ev.type === 'mount' || ev.type === 'update') {
|
|
288
|
-
const list = perComponent.get(ev.component) || []
|
|
289
|
-
list.push(ev)
|
|
290
|
-
perComponent.set(ev.component, list)
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 1. Re-render storms
|
|
295
|
-
for (const [name, events] of Array.from(perComponent.entries())) {
|
|
296
|
-
if (events.length < 5) continue
|
|
297
|
-
|
|
298
|
-
// Check any 1-second window
|
|
299
|
-
for (let i = 0; i < events.length - RENDER_STORM_THRESHOLD; i++) {
|
|
300
|
-
const windowEnd = events[i].timestamp + 1000
|
|
301
|
-
const count = events.filter(e => e.timestamp >= events[i].timestamp && e.timestamp <= windowEnd).length
|
|
302
|
-
if (count >= RENDER_STORM_THRESHOLD) {
|
|
303
|
-
result.storms.push(`${name}: ${count} renders within 1s window (threshold: ${RENDER_STORM_THRESHOLD})`)
|
|
304
|
-
break
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// 2. Cascading re-renders (rapid parentāchild chain)
|
|
310
|
-
const allEvents = state.events.filter(e => e.type === 'mount' || e.type === 'update')
|
|
311
|
-
for (let i = 1; i < allEvents.length; i++) {
|
|
312
|
-
const interval = allEvents[i].timestamp - allEvents[i - 1].timestamp
|
|
313
|
-
if (interval < CASCADE_THRESHOLD && allEvents[i].component !== allEvents[i - 1].component) {
|
|
314
|
-
result.cascades.push(
|
|
315
|
-
`${allEvents[i - 1].component} ā ${allEvents[i].component} (${interval.toFixed(1)}ms)`
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// 3. Infinite render loop detection (same component in rapid succession)
|
|
321
|
-
let loopStreak = 0
|
|
322
|
-
for (let i = 1; i < allEvents.length; i++) {
|
|
323
|
-
if (
|
|
324
|
-
allEvents[i].component === allEvents[i - 1].component &&
|
|
325
|
-
allEvents[i].timestamp - allEvents[i - 1].timestamp < 16 // < 1 frame
|
|
326
|
-
) {
|
|
327
|
-
loopStreak++
|
|
328
|
-
if (loopStreak >= 5) {
|
|
329
|
-
result.loops = true
|
|
330
|
-
result.storms.push(`${allEvents[i].component}: POSSIBLE INFINITE LOOP (${loopStreak} renders in <16ms each)`)
|
|
331
|
-
break
|
|
332
|
-
}
|
|
333
|
-
} else {
|
|
334
|
-
loopStreak = 0
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// 4. Streaming latency
|
|
339
|
-
const subEvents = state.events.filter(e => e.type === 'store-sub' && e.timestamp > 0)
|
|
340
|
-
const renderEvents = state.events.filter(e => e.type === 'update' && e.component === 'MessageRenderer')
|
|
341
|
-
if (subEvents.length > 0 && renderEvents.length > 0) {
|
|
342
|
-
const avg = renderEvents.length / (subEvents.length || 1)
|
|
343
|
-
if (avg < 0.5) {
|
|
344
|
-
result.streamingIssues.push(`Store subscriptions (${subEvents.length}) vs renders (${renderEvents.length}): possible skipped updates (ratio: ${avg.toFixed(2)})`)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// RAF call rate
|
|
349
|
-
if (rafCallCount > 60) {
|
|
350
|
-
result.streamingIssues.push(`High RAF call rate: ${rafCallCount}/sec (should be ~60 max)`)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Summary
|
|
354
|
-
for (const [name, events] of Array.from(perComponent.entries())) {
|
|
355
|
-
result.summary[name] = events.length
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Deduplicate using forEach
|
|
359
|
-
const stormSet = new Set<string>();
|
|
360
|
-
result.storms.forEach(s => stormSet.add(s));
|
|
361
|
-
result.storms = Array.from(stormSet);
|
|
362
|
-
result.cascades = result.cascades.slice(0, 20)
|
|
363
|
-
|
|
364
|
-
return result
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// ============ Reporting ============
|
|
368
|
-
|
|
369
|
-
function formatDuration(ms: number): string {
|
|
370
|
-
if (ms < 1) return `${(ms * 1000).toFixed(0)}μs`
|
|
371
|
-
if (ms < 1000) return `${ms.toFixed(1)}ms`
|
|
372
|
-
return `${(ms / 1000).toFixed(1)}s`
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function generateReport(): string {
|
|
376
|
-
const analysis = analyzeRenderEvents()
|
|
377
|
-
const lines: string[] = []
|
|
378
|
-
const now = new Date().toISOString()
|
|
379
|
-
|
|
380
|
-
lines.push(`\n${'ā'.repeat(60)}`)
|
|
381
|
-
lines.push(` AEGISCode Rendering Report ā ${now}`)
|
|
382
|
-
lines.push(`${'ā'.repeat(60)}`)
|
|
383
|
-
|
|
384
|
-
// Summary
|
|
385
|
-
lines.push(`\n Total render events tracked: ${state.events.length}`)
|
|
386
|
-
lines.push(` Components tracked: ${Object.keys(analysis.summary).length}`)
|
|
387
|
-
|
|
388
|
-
lines.push(`\n āā Render counts per component āā`)
|
|
389
|
-
const sorted = Object.entries(analysis.summary).sort((a, b) => b[1] - a[1])
|
|
390
|
-
for (const [name, count] of sorted.slice(0, 15)) {
|
|
391
|
-
lines.push(` ${name.padEnd(30)} ${String(count).padStart(5)} renders`)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Storms
|
|
395
|
-
if (analysis.storms.length > 0) {
|
|
396
|
-
lines.push(`\n āā ā RENDER STORMS āā`)
|
|
397
|
-
for (const storm of analysis.storms) {
|
|
398
|
-
lines.push(` š“ ${storm}`)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Cascades
|
|
403
|
-
if (analysis.cascades.length > 0) {
|
|
404
|
-
lines.push(`\n āā Re-render Cascades (top 10) āā`)
|
|
405
|
-
for (const cascade of analysis.cascades.slice(0, 10)) {
|
|
406
|
-
lines.push(` š” ${cascade}`)
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Infinite loops
|
|
411
|
-
if (analysis.loops) {
|
|
412
|
-
lines.push(`\n āā š“ INFINITE RENDER LOOP DETECTED āā`)
|
|
413
|
-
lines.push(` Immediate action required: check state updates in render cycle`)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Streaming issues
|
|
417
|
-
if (analysis.streamingIssues.length > 0) {
|
|
418
|
-
lines.push(`\n āā Streaming Issues āā`)
|
|
419
|
-
for (const issue of analysis.streamingIssues) {
|
|
420
|
-
lines.push(` š ${issue}`)
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Healthy
|
|
425
|
-
if (
|
|
426
|
-
analysis.storms.length === 0 &&
|
|
427
|
-
analysis.cascades.length === 0 &&
|
|
428
|
-
!analysis.loops &&
|
|
429
|
-
analysis.streamingIssues.length === 0
|
|
430
|
-
) {
|
|
431
|
-
lines.push(`\n ā
No rendering problems detected.`)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Recommendations
|
|
435
|
-
if (
|
|
436
|
-
analysis.storms.length > 0 ||
|
|
437
|
-
analysis.cascades.length > 0 ||
|
|
438
|
-
analysis.loops ||
|
|
439
|
-
analysis.streamingIssues.length > 0
|
|
440
|
-
) {
|
|
441
|
-
lines.push(`\n āā Recommendations āā`)
|
|
442
|
-
|
|
443
|
-
if (analysis.loops) {
|
|
444
|
-
lines.push(` 1. Break renderāsetStateārender cycle with useRef or useEffect`)
|
|
445
|
-
}
|
|
446
|
-
if (analysis.storms.length > 0) {
|
|
447
|
-
lines.push(` 2. Add React.memo() or refine memo comparator on storm components`)
|
|
448
|
-
lines.push(` 3. Batch store updates: use flush() pattern (already in AegisInterface)`)
|
|
449
|
-
}
|
|
450
|
-
if (analysis.cascades.length > 0) {
|
|
451
|
-
lines.push(` 4. Lift state up or use useContext with stable references`)
|
|
452
|
-
lines.push(` 5. Check useCallback/useMemo deps on parent components`)
|
|
453
|
-
}
|
|
454
|
-
if (analysis.streamingIssues.length > 0) {
|
|
455
|
-
lines.push(` 6. Ensure RAF poll catches content length changes (MessageList)`)
|
|
456
|
-
lines.push(` 7. Verify MessageRenderer comparator allows streaming updates`)
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
lines.push(`${'ā'.repeat(60)}\n`)
|
|
461
|
-
|
|
462
|
-
return lines.join('\n')
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function autoReport() {
|
|
466
|
-
if (!state.enabled) return
|
|
467
|
-
const report = generateReport()
|
|
468
|
-
console.log(report)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// ============ Public API ============
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Start the rendering debugger.
|
|
475
|
-
* Patches React.createElement, RAF, console, and zustand store.
|
|
476
|
-
* Begins periodic reporting of rendering health.
|
|
477
|
-
*
|
|
478
|
-
* @param options.reportInterval - ms between auto-reports (default 3000)
|
|
479
|
-
* @param options.verbose - console.log every render event (default false)
|
|
480
|
-
*/
|
|
481
|
-
export async function startRenderDebugger(options?: { reportInterval?: number; verbose?: boolean }): Promise<void> {
|
|
482
|
-
if (state.enabled) {
|
|
483
|
-
console.log('[RenderDebugger] Already running')
|
|
484
|
-
return
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
state.enabled = true
|
|
488
|
-
const interval = options?.reportInterval || REPORT_INTERVAL
|
|
489
|
-
const verbose = options?.verbose || false
|
|
490
|
-
|
|
491
|
-
console.log(`\nš AEGISCode Rendering Debugger started (report interval: ${interval}ms)`)
|
|
492
|
-
if (verbose) console.log(' Verbose mode: every render event will be logged')
|
|
493
|
-
|
|
494
|
-
// Patch RAF
|
|
495
|
-
const unpatches: (() => void)[] = []
|
|
496
|
-
unpatches.push(patchRAF())
|
|
497
|
-
|
|
498
|
-
// Patch console for React warnings
|
|
499
|
-
unpatches.push(patchConsole())
|
|
500
|
-
|
|
501
|
-
// Try to patch React if available
|
|
502
|
-
try {
|
|
503
|
-
const React = await import('react')
|
|
504
|
-
unpatches.push(patchReactCreateElement(React.default || React))
|
|
505
|
-
} catch {
|
|
506
|
-
// React not loaded yet; try again after a tick
|
|
507
|
-
setTimeout(async () => {
|
|
508
|
-
try {
|
|
509
|
-
const React = await import('react')
|
|
510
|
-
unpatches.push(patchReactCreateElement(React.default || React))
|
|
511
|
-
} catch {}
|
|
512
|
-
}, 100)
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// Auto-reporting interval
|
|
516
|
-
state.intervalId = setInterval(() => {
|
|
517
|
-
autoReport()
|
|
518
|
-
}, interval)
|
|
519
|
-
|
|
520
|
-
// Store reference for cleanup
|
|
521
|
-
;(globalThis as any).__RENDER_DEBUGGER__ = {
|
|
522
|
-
state,
|
|
523
|
-
unpatches,
|
|
524
|
-
stop: stopRenderDebugger,
|
|
525
|
-
report: generateReport,
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (!verbose) {
|
|
529
|
-
// In non-verbose mode, suppress individual render event logging
|
|
530
|
-
const origPush = state.events.push.bind(state.events)
|
|
531
|
-
state.events.push = (...items: RenderEvent[]): number => {
|
|
532
|
-
// Only log warnings to console
|
|
533
|
-
for (const item of items) {
|
|
534
|
-
if (item.stack?.includes('Warning') || item.component.startsWith('[React')) {
|
|
535
|
-
console.log(`[RenderDebugger] ${item.component}: ${item.nextProps?.slice(0, 80)}`)
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return origPush(...items)
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Stop the rendering debugger and generate a final report.
|
|
545
|
-
*/
|
|
546
|
-
export function stopRenderDebugger(): string {
|
|
547
|
-
if (!state.enabled) {
|
|
548
|
-
return 'Render debugger was not running.'
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
state.enabled = false
|
|
552
|
-
|
|
553
|
-
// Restore patches
|
|
554
|
-
const debug = (globalThis as any).__RENDER_DEBUGGER__
|
|
555
|
-
if (debug?.unpatches) {
|
|
556
|
-
for (const unpatch of debug.unpatches) {
|
|
557
|
-
try { unpatch() } catch {}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Clear interval
|
|
562
|
-
if (state.intervalId) {
|
|
563
|
-
clearInterval(state.intervalId)
|
|
564
|
-
state.intervalId = null
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
delete (globalThis as any).__RENDER_DEBUGGER__
|
|
568
|
-
|
|
569
|
-
const finalReport = generateReport()
|
|
570
|
-
console.log(finalReport)
|
|
571
|
-
|
|
572
|
-
console.log('š Render Debugger stopped.')
|
|
573
|
-
console.log(` Events captured: ${state.events.length}`)
|
|
574
|
-
|
|
575
|
-
// Reset state
|
|
576
|
-
state.events = []
|
|
577
|
-
state.componentStats.clear()
|
|
578
|
-
state.componentRenderCounts.clear()
|
|
579
|
-
|
|
580
|
-
return finalReport
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Get the current analysis without stopping the debugger.
|
|
585
|
-
*/
|
|
586
|
-
export function getRenderReport(): string {
|
|
587
|
-
return generateReport()
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* Get raw render events for programmatic analysis.
|
|
592
|
-
*/
|
|
593
|
-
export function getRenderEvents(): RenderEvent[] {
|
|
594
|
-
return [...state.events]
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Get the top N most frequently rendered components.
|
|
599
|
-
*/
|
|
600
|
-
export function getHottestComponents(n = 10): Array<{ name: string; count: number }> {
|
|
601
|
-
const counts: Record<string, number> = {}
|
|
602
|
-
for (const ev of state.events) {
|
|
603
|
-
if (ev.type === 'mount' || ev.type === 'update') {
|
|
604
|
-
counts[ev.component] = (counts[ev.component] || 0) + 1
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return Object.entries(counts)
|
|
608
|
-
.sort((a, b) => b[1] - a[1])
|
|
609
|
-
.slice(0, n)
|
|
610
|
-
.map(([name, count]) => ({ name, count }))
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// ============ Auto-start via CLI flag ============
|
|
614
|
-
|
|
615
|
-
// Check for --debug-rendering flag
|
|
616
|
-
const debugRenderingArg = process.argv.includes('--debug-rendering')
|
|
617
|
-
if (debugRenderingArg) {
|
|
618
|
-
setTimeout(() => startRenderDebugger({ verbose: false }), 0)
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
export default { startRenderDebugger, stopRenderDebugger, getRenderReport }
|