aegiscode 3.1.8 → 3.1.10
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/README.md +23 -15
- package/dist/main.js +549 -552
- package/package.json +4 -2
- package/src/agent/Agent.ts +903 -0
- package/src/agent/SimpleAgent.ts +48 -0
- package/src/agent/index.ts +54 -0
- package/src/agent/orchestrator/AppBuilder.ts +443 -0
- package/src/agent/orchestrator/CouncilAgent.ts +310 -0
- package/src/agent/orchestrator/DiscussionRoom.ts +462 -0
- package/src/agent/orchestrator/OrchestratorAgent.ts +637 -0
- package/src/agent/orchestrator/index.ts +38 -0
- package/src/agent/orchestrator/utils.ts +397 -0
- package/src/agent/pricing.ts +115 -0
- package/src/agent/router.ts +74 -0
- package/src/agent/routerStats.ts +121 -0
- package/src/agent/types.ts +318 -0
- package/src/auth/login.ts +383 -0
- package/src/cli/config.ts +189 -0
- package/src/cli/index.ts +17 -0
- package/src/cli/middleware.ts +119 -0
- package/src/cli/types.ts +75 -0
- package/src/config/ConfigManager.ts +587 -0
- package/src/config/index.ts +7 -0
- package/src/config/types.ts +584 -0
- package/src/context/CompactionService.ts +300 -0
- package/src/context/ContextManager.ts +450 -0
- package/src/context/FileAnalyzer.ts +267 -0
- package/src/context/TokenCounter.ts +265 -0
- package/src/context/index.ts +27 -0
- package/src/context/storage/CacheStore.ts +176 -0
- package/src/context/storage/JSONLStore.ts +201 -0
- package/src/context/storage/MemoryStore.ts +205 -0
- package/src/context/storage/PersistentStore.ts +327 -0
- package/src/context/storage/index.ts +9 -0
- package/src/context/storage/pathUtils.ts +114 -0
- package/src/context/test.ts +309 -0
- package/src/context/types.ts +268 -0
- package/src/hooks/HookExecutor.ts +434 -0
- package/src/hooks/HookManager.ts +596 -0
- package/src/hooks/HookService.ts +269 -0
- package/src/hooks/Matcher.ts +157 -0
- package/src/hooks/index.ts +63 -0
- package/src/hooks/types.ts +424 -0
- package/src/main.tsx +596 -0
- package/src/mcp/HealthMonitor.ts +150 -0
- package/src/mcp/McpClient.ts +491 -0
- package/src/mcp/McpRegistry.ts +321 -0
- package/src/mcp/createMcpTool.ts +251 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/server.ts +334 -0
- package/src/mcp/test-server.ts +88 -0
- package/src/mcp/test.ts +372 -0
- package/src/mcp/types.ts +247 -0
- package/src/memory/AgentMemoryBus.ts +432 -0
- package/src/memory/CloudSync.ts +99 -0
- package/src/memory/DriveSync.ts +106 -0
- package/src/memory/SharedMemory.ts +951 -0
- package/src/memory/index.ts +14 -0
- package/src/memory/machineFingerprint.ts +40 -0
- package/src/orchestrator/SubAgentMetadata.ts +136 -0
- package/src/prompts/builder.ts +213 -0
- package/src/prompts/default.ts +144 -0
- package/src/prompts/index.ts +16 -0
- package/src/prompts/plan.ts +64 -0
- package/src/prompts/test.ts +78 -0
- package/src/services/AnthropicChatService.ts +341 -0
- package/src/services/ChatService.ts +347 -0
- package/src/services/ClaudeCliChatService.ts +256 -0
- package/src/services/CloudSync.ts +168 -0
- package/src/services/CostLedger.ts +211 -0
- package/src/services/Heartbeat.ts +135 -0
- package/src/services/LearningCollector.ts +291 -0
- package/src/services/OllamaInstaller.ts +342 -0
- package/src/services/VersionChecker.ts +445 -0
- package/src/services/index.ts +57 -0
- package/src/services/streaming/OpenAIEventAdapter.ts +126 -0
- package/src/services/streaming/RenderingProfile.ts +90 -0
- package/src/services/streaming/StreamEventParser.ts +181 -0
- package/src/services/streaming/ThrottledRenderer.ts +139 -0
- package/src/services/streaming/TranscriptBuffer.ts +574 -0
- package/src/services/streaming/eventStatusMap.ts +52 -0
- package/src/services/streaming/index.ts +46 -0
- package/src/services/streaming/renderFormatting.ts +79 -0
- package/src/services/streaming/types.ts +234 -0
- package/src/skills/SkillLoader.ts +126 -0
- package/src/skills/SkillRegistry.ts +366 -0
- package/src/skills/index.ts +48 -0
- package/src/skills/types.ts +146 -0
- package/src/slash-commands/billing.ts +70 -0
- package/src/slash-commands/build.ts +413 -0
- package/src/slash-commands/builtinCommands.ts +2733 -0
- package/src/slash-commands/clone.ts +242 -0
- package/src/slash-commands/council.ts +125 -0
- package/src/slash-commands/custom/CustomCommandExecutor.ts +143 -0
- package/src/slash-commands/custom/CustomCommandLoader.ts +251 -0
- package/src/slash-commands/custom/CustomCommandRegistry.ts +144 -0
- package/src/slash-commands/custom/index.ts +7 -0
- package/src/slash-commands/debate.ts +254 -0
- package/src/slash-commands/gmail.ts +105 -0
- package/src/slash-commands/index.ts +388 -0
- package/src/slash-commands/mcpCommand.ts +205 -0
- package/src/slash-commands/types.ts +201 -0
- package/src/store/index.ts +76 -0
- package/src/store/selectors.ts +246 -0
- package/src/store/slices/appSlice.ts +205 -0
- package/src/store/slices/commandSlice.ts +115 -0
- package/src/store/slices/configSlice.ts +46 -0
- package/src/store/slices/focusSlice.ts +64 -0
- package/src/store/slices/index.ts +9 -0
- package/src/store/slices/sessionSlice.ts +424 -0
- package/src/store/streaming-buffer.ts +425 -0
- package/src/store/test.ts +296 -0
- package/src/store/types.ts +274 -0
- package/src/store/vanilla.ts +186 -0
- package/src/tools/builtin/bash.ts +236 -0
- package/src/tools/builtin/council.ts +105 -0
- package/src/tools/builtin/edit.ts +213 -0
- package/src/tools/builtin/glob.ts +136 -0
- package/src/tools/builtin/grep.ts +263 -0
- package/src/tools/builtin/index.ts +61 -0
- package/src/tools/builtin/memory.ts +66 -0
- package/src/tools/builtin/read.ts +168 -0
- package/src/tools/builtin/skill.ts +97 -0
- package/src/tools/builtin/snapshot.ts +40 -0
- package/src/tools/builtin/task.ts +106 -0
- package/src/tools/builtin/write.ts +134 -0
- package/src/tools/createTool.ts +221 -0
- package/src/tools/execution/ExecutionPipeline.ts +263 -0
- package/src/tools/execution/index.ts +40 -0
- package/src/tools/execution/stages/CacheStage.ts +131 -0
- package/src/tools/execution/stages/ConfirmationStage.ts +203 -0
- package/src/tools/execution/stages/DiscoveryStage.ts +46 -0
- package/src/tools/execution/stages/ExecutionStage.ts +48 -0
- package/src/tools/execution/stages/FormattingStage.ts +44 -0
- package/src/tools/execution/stages/HookStage.ts +72 -0
- package/src/tools/execution/stages/PermissionStage.ts +287 -0
- package/src/tools/execution/stages/PostHookStage.ts +71 -0
- package/src/tools/execution/stages/index.ts +12 -0
- package/src/tools/execution/test.ts +266 -0
- package/src/tools/execution/types.ts +273 -0
- package/src/tools/index.ts +81 -0
- package/src/tools/registry.ts +304 -0
- package/src/tools/schemas.ts +109 -0
- package/src/tools/test.ts +220 -0
- package/src/tools/types.ts +175 -0
- package/src/tools/validation/PermissionChecker.ts +242 -0
- package/src/tools/validation/SensitiveFileDetector.ts +210 -0
- package/src/tools/validation/index.ts +11 -0
- package/src/ui/App.tsx +166 -0
- package/src/ui/components/AegisInterface.tsx +484 -0
- package/src/ui/components/common/ChatSearch.tsx +150 -0
- package/src/ui/components/common/ErrorBoundary.tsx +82 -0
- package/src/ui/components/common/ExitMessage.tsx +120 -0
- package/src/ui/components/common/LoadingIndicator.tsx +49 -0
- package/src/ui/components/common/index.ts +6 -0
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +208 -0
- package/src/ui/components/dialog/InteractiveSelector.tsx +149 -0
- package/src/ui/components/dialog/SetupWizard.tsx +297 -0
- package/src/ui/components/dialog/UpdatePrompt.tsx +155 -0
- package/src/ui/components/dialog/index.ts +8 -0
- package/src/ui/components/index.ts +28 -0
- package/src/ui/components/input/CommandSuggestions.tsx +139 -0
- package/src/ui/components/input/CustomTextInput.tsx +220 -0
- package/src/ui/components/input/InputArea.tsx +361 -0
- package/src/ui/components/input/PromptSuggestions.tsx +66 -0
- package/src/ui/components/input/index.ts +6 -0
- package/src/ui/components/layout/ChatStatusBar.tsx +90 -0
- package/src/ui/components/layout/ContextBar.tsx +79 -0
- package/src/ui/components/layout/MessageArea.tsx +96 -0
- package/src/ui/components/layout/MessageList.tsx +647 -0
- package/src/ui/components/layout/MessageSeparator.tsx +26 -0
- package/src/ui/components/layout/WelcomeMessage.tsx +93 -0
- package/src/ui/components/layout/index.ts +7 -0
- package/src/ui/components/markdown/CodeHighlighter.tsx +292 -0
- package/src/ui/components/markdown/MessageRenderer.tsx +1211 -0
- package/src/ui/components/markdown/index.ts +8 -0
- package/src/ui/components/markdown/parser.ts +336 -0
- package/src/ui/components/markdown/types.ts +66 -0
- package/src/ui/focus/FocusManager.ts +137 -0
- package/src/ui/focus/index.ts +13 -0
- package/src/ui/focus/types.ts +54 -0
- package/src/ui/focus/useFocus.ts +75 -0
- package/src/ui/hooks/index.ts +11 -0
- package/src/ui/hooks/useAgent.ts +284 -0
- package/src/ui/hooks/useCommandHistory.ts +87 -0
- package/src/ui/hooks/useCommandProcessor.ts +443 -0
- package/src/ui/hooks/useConfirmation.ts +99 -0
- package/src/ui/hooks/useCtrlCHandler.ts +100 -0
- package/src/ui/hooks/useInputBuffer.ts +122 -0
- package/src/ui/hooks/useTerminalSize.ts +68 -0
- package/src/ui/hooks/useTerminalWidth.ts +5 -0
- package/src/ui/hooks/useWindowedList.ts +118 -0
- package/src/ui/render-debugger.ts +621 -0
- package/src/ui/test.ts +189 -0
- package/src/ui/themes/ThemeManager.ts +332 -0
- package/src/ui/themes/aegisTheme.ts +87 -0
- package/src/ui/themes/darkTheme.ts +87 -0
- package/src/ui/themes/defaultTheme.ts +85 -0
- package/src/ui/themes/index.ts +10 -0
- package/src/ui/themes/lightTheme.ts +89 -0
- package/src/ui/themes/popularThemes.ts +187 -0
- package/src/ui/themes/types.ts +130 -0
- package/src/utils/clipboard.ts +48 -0
- package/src/utils/debug.ts +43 -0
- package/src/utils/environment.ts +68 -0
- package/src/utils/index.ts +10 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamEventParser — replicates Claude Code's event_parser.py
|
|
3
|
+
*
|
|
4
|
+
* Parses raw Anthropic Messages API streaming events (content_block_start/delta/stop)
|
|
5
|
+
* into typed internal events that the TranscriptBuffer consumes.
|
|
6
|
+
*
|
|
7
|
+
* This is the entry point for all streaming content. Every chunk from the API
|
|
8
|
+
* stream passes through here before being applied to the transcript.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
AnthropicStreamEvent,
|
|
13
|
+
ParsedEvent,
|
|
14
|
+
ContentBlockStartEvent,
|
|
15
|
+
ContentBlockDeltaEvent,
|
|
16
|
+
MessageStartEvent,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse a raw Anthropic stream event into one or more internal events.
|
|
21
|
+
*
|
|
22
|
+
* Returns an array of ParsedEvents (most events produce 1, content_block_start
|
|
23
|
+
* may produce 2 if there's initial text). Empty array if not recognized.
|
|
24
|
+
*/
|
|
25
|
+
export function parseStreamEvent(event: AnthropicStreamEvent): ParsedEvent[] {
|
|
26
|
+
const results: ParsedEvent[] = [];
|
|
27
|
+
|
|
28
|
+
switch (event.type) {
|
|
29
|
+
case 'content_block_start':
|
|
30
|
+
return parseContentBlockStart(event);
|
|
31
|
+
|
|
32
|
+
case 'content_block_delta':
|
|
33
|
+
return parseContentBlockDelta(event);
|
|
34
|
+
|
|
35
|
+
case 'content_block_stop':
|
|
36
|
+
return [{ type: 'block_stop', index: event.index }];
|
|
37
|
+
|
|
38
|
+
case 'message_start':
|
|
39
|
+
// message_start contains initial content blocks in non-streaming form.
|
|
40
|
+
// If the API sends content inline, parse them as full blocks.
|
|
41
|
+
return parseMessageContent(event);
|
|
42
|
+
|
|
43
|
+
case 'message_delta':
|
|
44
|
+
// message_delta signals completion metadata — map to complete event.
|
|
45
|
+
return [{ type: 'complete', status: event.delta.stop_reason }];
|
|
46
|
+
|
|
47
|
+
case 'message_stop':
|
|
48
|
+
// Signal final completion. If no content was streamed, this ensures
|
|
49
|
+
// the transcript shows something.
|
|
50
|
+
return [{ type: 'complete', status: 'message_stop' }];
|
|
51
|
+
|
|
52
|
+
case 'ping':
|
|
53
|
+
return [];
|
|
54
|
+
|
|
55
|
+
case 'error':
|
|
56
|
+
return [{
|
|
57
|
+
type: 'error',
|
|
58
|
+
message: event.error?.message || 'Unknown API error',
|
|
59
|
+
}];
|
|
60
|
+
|
|
61
|
+
default:
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse content_block_start — produces the start event for the block type,
|
|
68
|
+
* plus potentially initial text/thinking content.
|
|
69
|
+
*/
|
|
70
|
+
function parseContentBlockStart(event: ContentBlockStartEvent): ParsedEvent[] {
|
|
71
|
+
const block = event.content_block;
|
|
72
|
+
const results: ParsedEvent[] = [];
|
|
73
|
+
|
|
74
|
+
switch (block.type) {
|
|
75
|
+
case 'thinking':
|
|
76
|
+
results.push({ type: 'thinking_start', index: event.index });
|
|
77
|
+
if (block.thinking) {
|
|
78
|
+
results.push({ type: 'thinking_delta', index: event.index, text: block.thinking });
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case 'text':
|
|
83
|
+
results.push({ type: 'text_start', index: event.index });
|
|
84
|
+
if (block.text) {
|
|
85
|
+
results.push({ type: 'text_delta', index: event.index, text: block.text });
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'tool_use':
|
|
90
|
+
results.push({
|
|
91
|
+
type: 'tool_use_start',
|
|
92
|
+
index: event.index,
|
|
93
|
+
id: block.id,
|
|
94
|
+
name: block.name,
|
|
95
|
+
input: block.input,
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Parse content_block_delta — produces delta events for the block type.
|
|
105
|
+
*/
|
|
106
|
+
function parseContentBlockDelta(event: ContentBlockDeltaEvent): ParsedEvent[] {
|
|
107
|
+
const delta = event.delta;
|
|
108
|
+
|
|
109
|
+
switch (delta.type) {
|
|
110
|
+
case 'text_delta':
|
|
111
|
+
return [{
|
|
112
|
+
type: 'text_delta',
|
|
113
|
+
index: event.index,
|
|
114
|
+
text: delta.text,
|
|
115
|
+
}];
|
|
116
|
+
|
|
117
|
+
case 'thinking_delta':
|
|
118
|
+
return [{
|
|
119
|
+
type: 'thinking_delta',
|
|
120
|
+
index: event.index,
|
|
121
|
+
text: delta.thinking,
|
|
122
|
+
}];
|
|
123
|
+
|
|
124
|
+
case 'input_json_delta':
|
|
125
|
+
return [{
|
|
126
|
+
type: 'tool_use_delta',
|
|
127
|
+
index: event.index,
|
|
128
|
+
partial_json: delta.partial_json,
|
|
129
|
+
}];
|
|
130
|
+
|
|
131
|
+
default:
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Parse message content from message_start event — handles non-streaming
|
|
138
|
+
* content blocks that arrive inline.
|
|
139
|
+
*/
|
|
140
|
+
function parseMessageContent(event: MessageStartEvent): ParsedEvent[] {
|
|
141
|
+
const content = event.message?.content;
|
|
142
|
+
if (!Array.isArray(content) || content.length === 0) return [];
|
|
143
|
+
|
|
144
|
+
const results: ParsedEvent[] = [];
|
|
145
|
+
|
|
146
|
+
for (const block of content) {
|
|
147
|
+
if (typeof block !== 'object' || block === null) continue;
|
|
148
|
+
|
|
149
|
+
const b = block as Record<string, unknown>;
|
|
150
|
+
|
|
151
|
+
switch (b.type) {
|
|
152
|
+
case 'text':
|
|
153
|
+
results.push({ type: 'text_chunk' as const, text: String(b.text || '') });
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
case 'thinking':
|
|
157
|
+
results.push({ type: 'thinking_chunk' as const, text: String(b.thinking || '') });
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'tool_use':
|
|
161
|
+
results.push({
|
|
162
|
+
type: 'tool_use' as const,
|
|
163
|
+
id: String(b.id || '').trim(),
|
|
164
|
+
name: String(b.name || ''),
|
|
165
|
+
input: b.input,
|
|
166
|
+
});
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case 'tool_result':
|
|
170
|
+
results.push({
|
|
171
|
+
type: 'tool_result' as const,
|
|
172
|
+
tool_use_id: String(b.tool_use_id || '').trim(),
|
|
173
|
+
content: b.content,
|
|
174
|
+
is_error: Boolean(b.is_error),
|
|
175
|
+
});
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThrottledRenderer — rate-limited transcript rendering with status lines.
|
|
3
|
+
*
|
|
4
|
+
* Replicates free-claude-code's ThrottledTranscriptEditor (messaging/ui_updates.py)
|
|
5
|
+
* plus the UI update loop from node_event_pipeline.py process_parsed_cli_event().
|
|
6
|
+
*
|
|
7
|
+
* Key behaviors:
|
|
8
|
+
* 1. Throttles to 1 update per second (configurable)
|
|
9
|
+
* 2. Deduplicates — skips if rendered text hasn't changed
|
|
10
|
+
* 3. Handles status line attachment
|
|
11
|
+
* 4. Integrates with RenderingProfile for platform-specific formatting
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { TranscriptBuffer } from './TranscriptBuffer.js';
|
|
15
|
+
import type { RenderContext } from './types.js';
|
|
16
|
+
import type { RenderingProfile } from './types.js';
|
|
17
|
+
|
|
18
|
+
/** Callback type for delivering rendered output */
|
|
19
|
+
export type RenderOutputCallback = (text: string) => void | Promise<void>;
|
|
20
|
+
|
|
21
|
+
export interface ThrottledRendererOptions {
|
|
22
|
+
/** Throttle interval in ms (default 1000) */
|
|
23
|
+
throttleMs?: number;
|
|
24
|
+
/** Debug logging */
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ThrottledRenderer — drives transcript rendering with rate limiting.
|
|
30
|
+
*
|
|
31
|
+
* Usage:
|
|
32
|
+
* const renderer = new ThrottledRenderer(transcript, profile, outputCallback);
|
|
33
|
+
* await renderer.update('🔄 Processing...'); // throttled
|
|
34
|
+
* await renderer.update('✅ Complete', true); // forced (no throttle)
|
|
35
|
+
*/
|
|
36
|
+
export class ThrottledRenderer {
|
|
37
|
+
private _transcript: TranscriptBuffer;
|
|
38
|
+
private _renderCtx: RenderContext;
|
|
39
|
+
private _outputCallback: RenderOutputCallback;
|
|
40
|
+
private _throttleMs: number;
|
|
41
|
+
private _debug: boolean;
|
|
42
|
+
|
|
43
|
+
private _lastUpdate = 0;
|
|
44
|
+
private _lastDisplayedText: string | null = null;
|
|
45
|
+
private _lastStatus: string | null = null;
|
|
46
|
+
private _pendingTimer: ReturnType<typeof setTimeout> | null = null;
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
transcript: TranscriptBuffer,
|
|
50
|
+
profile: RenderingProfile,
|
|
51
|
+
outputCallback: RenderOutputCallback,
|
|
52
|
+
options: ThrottledRendererOptions = {},
|
|
53
|
+
) {
|
|
54
|
+
this._transcript = transcript;
|
|
55
|
+
this._renderCtx = profile.renderCtx;
|
|
56
|
+
this._outputCallback = outputCallback;
|
|
57
|
+
this._throttleMs = options.throttleMs ?? 1000;
|
|
58
|
+
this._debug = options.debug ?? false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get lastStatus(): string | null {
|
|
62
|
+
return this._lastStatus;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Update the rendered output.
|
|
67
|
+
* @param status Optional status line to append below transcript
|
|
68
|
+
* @param force If true, skip throttling and render immediately
|
|
69
|
+
*/
|
|
70
|
+
async update(status?: string | null, force = false): Promise<void> {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
|
|
73
|
+
if (status !== undefined && status !== null) {
|
|
74
|
+
this._lastStatus = status;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!force && now - this._lastUpdate < this._throttleMs) {
|
|
78
|
+
// Schedule a pending update in case no more events arrive
|
|
79
|
+
if (!this._pendingTimer) {
|
|
80
|
+
this._pendingTimer = setTimeout(() => {
|
|
81
|
+
this._pendingTimer = null;
|
|
82
|
+
this._doUpdate().catch(() => {});
|
|
83
|
+
}, this._throttleMs);
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Clear any pending timer
|
|
89
|
+
if (this._pendingTimer) {
|
|
90
|
+
clearTimeout(this._pendingTimer);
|
|
91
|
+
this._pendingTimer = null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await this._doUpdate();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async _doUpdate(): Promise<void> {
|
|
98
|
+
this._lastUpdate = Date.now();
|
|
99
|
+
|
|
100
|
+
let display: string;
|
|
101
|
+
try {
|
|
102
|
+
display = this._transcript.render(this._renderCtx, this._lastStatus ?? undefined);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (this._debug) {
|
|
105
|
+
console.error('[ThrottledRenderer] render failed:', err);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!display && !this._lastDisplayedText) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (display === this._lastDisplayedText) {
|
|
115
|
+
return; // dedup
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this._lastDisplayedText = display;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await this._outputCallback(display);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (this._debug) {
|
|
124
|
+
console.error('[ThrottledRenderer] output callback failed:', err);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Reset internal state (for new turns) */
|
|
130
|
+
reset(): void {
|
|
131
|
+
this._lastUpdate = 0;
|
|
132
|
+
this._lastDisplayedText = null;
|
|
133
|
+
this._lastStatus = null;
|
|
134
|
+
if (this._pendingTimer) {
|
|
135
|
+
clearTimeout(this._pendingTimer);
|
|
136
|
+
this._pendingTimer = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|