wave-code 1.0.10 → 1.1.0
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/wave-code.js +8 -2
- package/dist/bundle/wave.mjs +3207 -0
- package/package.json +24 -25
- package/src/commands/update.ts +2 -7
- package/src/components/App.tsx +3 -7
- package/src/components/BtwDisplay.tsx +10 -1
- package/src/components/ChatInterface.tsx +21 -52
- package/src/components/ConfirmationDetails.tsx +144 -37
- package/src/components/DiffDisplay.tsx +335 -301
- package/src/components/InputBox.tsx +3 -1
- package/src/components/LoadingIndicator.tsx +16 -2
- package/src/components/MessageBlockItem.tsx +2 -4
- package/src/components/MessageList.tsx +75 -14
- package/src/components/ReasoningDisplay.tsx +2 -4
- package/src/components/SessionSelector.tsx +82 -11
- package/src/components/StatusCommand.tsx +2 -9
- package/src/components/ToolDisplay.tsx +2 -4
- package/src/contexts/useChat.tsx +156 -335
- package/src/daemon/commands.ts +57 -27
- package/src/daemon/jsonRpcClient.ts +1 -1
- package/src/daemon/socketClient.ts +2 -2
- package/src/hooks/useInputManager.ts +1 -1
- package/src/index.ts +57 -10
- package/src/managers/inputReducer.ts +186 -104
- package/src/print-cli.ts +21 -17
- package/src/reducers/selectorReducer.ts +10 -0
- package/src/session-selector-cli.tsx +269 -28
- package/src/stdio/agentBridge.ts +22 -21
- package/src/stdio/daemonServer.ts +3 -1
- package/src/utils/clipboard.ts +21 -0
- package/src/utils/highlightUtils.ts +46 -1
- package/src/utils/readPackageJson.ts +29 -0
- package/src/utils/streamingText.ts +11 -0
- package/src/utils/worktree.ts +191 -43
- package/dist/cli.d.ts +0 -6
- package/dist/cli.js +0 -53
- package/dist/commands/plugin/disable.d.ts +0 -5
- package/dist/commands/plugin/disable.js +0 -15
- package/dist/commands/plugin/enable.d.ts +0 -5
- package/dist/commands/plugin/enable.js +0 -15
- package/dist/commands/plugin/install.d.ts +0 -5
- package/dist/commands/plugin/install.js +0 -21
- package/dist/commands/plugin/list.d.ts +0 -1
- package/dist/commands/plugin/list.js +0 -31
- package/dist/commands/plugin/marketplace.d.ts +0 -10
- package/dist/commands/plugin/marketplace.js +0 -90
- package/dist/commands/plugin/uninstall.d.ts +0 -3
- package/dist/commands/plugin/uninstall.js +0 -16
- package/dist/commands/plugin/update.d.ts +0 -3
- package/dist/commands/plugin/update.js +0 -15
- package/dist/commands/update.d.ts +0 -1
- package/dist/commands/update.js +0 -97
- package/dist/components/AgentsManager.d.ts +0 -7
- package/dist/components/AgentsManager.js +0 -109
- package/dist/components/App.d.ts +0 -9
- package/dist/components/App.js +0 -76
- package/dist/components/BackgroundTaskManager.d.ts +0 -5
- package/dist/components/BackgroundTaskManager.js +0 -99
- package/dist/components/BangDisplay.d.ts +0 -8
- package/dist/components/BangDisplay.js +0 -17
- package/dist/components/BtwDisplay.d.ts +0 -7
- package/dist/components/BtwDisplay.js +0 -19
- package/dist/components/ChatInterface.d.ts +0 -2
- package/dist/components/ChatInterface.js +0 -61
- package/dist/components/CommandSelector.d.ts +0 -10
- package/dist/components/CommandSelector.js +0 -66
- package/dist/components/CompactDisplay.d.ts +0 -8
- package/dist/components/CompactDisplay.js +0 -13
- package/dist/components/ConfirmationDetails.d.ts +0 -9
- package/dist/components/ConfirmationDetails.js +0 -45
- package/dist/components/ConfirmationSelector.d.ts +0 -13
- package/dist/components/ConfirmationSelector.js +0 -127
- package/dist/components/DiffDisplay.d.ts +0 -8
- package/dist/components/DiffDisplay.js +0 -201
- package/dist/components/DiscoverView.d.ts +0 -2
- package/dist/components/DiscoverView.js +0 -37
- package/dist/components/FileSelector.d.ts +0 -11
- package/dist/components/FileSelector.js +0 -50
- package/dist/components/HelpView.d.ts +0 -7
- package/dist/components/HelpView.js +0 -74
- package/dist/components/HistorySearch.d.ts +0 -8
- package/dist/components/HistorySearch.js +0 -66
- package/dist/components/InputBox.d.ts +0 -24
- package/dist/components/InputBox.js +0 -149
- package/dist/components/InstalledView.d.ts +0 -2
- package/dist/components/InstalledView.js +0 -45
- package/dist/components/LoadingIndicator.d.ts +0 -10
- package/dist/components/LoadingIndicator.js +0 -6
- package/dist/components/LoginCommand.d.ts +0 -5
- package/dist/components/LoginCommand.js +0 -153
- package/dist/components/Markdown.d.ts +0 -5
- package/dist/components/Markdown.js +0 -127
- package/dist/components/MarketplaceAddForm.d.ts +0 -2
- package/dist/components/MarketplaceAddForm.js +0 -55
- package/dist/components/MarketplaceDetail.d.ts +0 -2
- package/dist/components/MarketplaceDetail.js +0 -74
- package/dist/components/MarketplaceList.d.ts +0 -8
- package/dist/components/MarketplaceList.js +0 -17
- package/dist/components/MarketplaceView.d.ts +0 -2
- package/dist/components/MarketplaceView.js +0 -41
- package/dist/components/McpManager.d.ts +0 -9
- package/dist/components/McpManager.js +0 -88
- package/dist/components/MessageBlockItem.d.ts +0 -8
- package/dist/components/MessageBlockItem.js +0 -15
- package/dist/components/MessageList.d.ts +0 -10
- package/dist/components/MessageList.js +0 -80
- package/dist/components/ModelSelector.d.ts +0 -8
- package/dist/components/ModelSelector.js +0 -45
- package/dist/components/Notifications.d.ts +0 -7
- package/dist/components/Notifications.js +0 -9
- package/dist/components/PlanDisplay.d.ts +0 -7
- package/dist/components/PlanDisplay.js +0 -6
- package/dist/components/PluginDetail.d.ts +0 -2
- package/dist/components/PluginDetail.js +0 -87
- package/dist/components/PluginList.d.ts +0 -13
- package/dist/components/PluginList.js +0 -15
- package/dist/components/PluginManagerShell.d.ts +0 -6
- package/dist/components/PluginManagerShell.js +0 -92
- package/dist/components/PluginManagerTypes.d.ts +0 -35
- package/dist/components/PluginManagerTypes.js +0 -1
- package/dist/components/QueuedMessageList.d.ts +0 -2
- package/dist/components/QueuedMessageList.js +0 -18
- package/dist/components/ReasoningDisplay.d.ts +0 -8
- package/dist/components/ReasoningDisplay.js +0 -13
- package/dist/components/RewindCommand.d.ts +0 -12
- package/dist/components/RewindCommand.js +0 -78
- package/dist/components/SessionSelector.d.ts +0 -12
- package/dist/components/SessionSelector.js +0 -47
- package/dist/components/StatusCommand.d.ts +0 -5
- package/dist/components/StatusCommand.js +0 -28
- package/dist/components/StatusLine.d.ts +0 -6
- package/dist/components/StatusLine.js +0 -9
- package/dist/components/TaskList.d.ts +0 -5
- package/dist/components/TaskList.js +0 -197
- package/dist/components/TaskNotificationMessage.d.ts +0 -5
- package/dist/components/TaskNotificationMessage.js +0 -12
- package/dist/components/ToolDisplay.d.ts +0 -9
- package/dist/components/ToolDisplay.js +0 -55
- package/dist/components/WorkflowManager.d.ts +0 -5
- package/dist/components/WorkflowManager.js +0 -102
- package/dist/components/WorktreeExitPrompt.d.ts +0 -12
- package/dist/components/WorktreeExitPrompt.js +0 -35
- package/dist/constants/commands.d.ts +0 -2
- package/dist/constants/commands.js +0 -92
- package/dist/contexts/PluginManagerContext.d.ts +0 -3
- package/dist/contexts/PluginManagerContext.js +0 -9
- package/dist/contexts/useAppConfig.d.ts +0 -10
- package/dist/contexts/useAppConfig.js +0 -13
- package/dist/contexts/useChat.d.ts +0 -101
- package/dist/contexts/useChat.js +0 -993
- package/dist/contracts/status.d.ts +0 -7
- package/dist/contracts/status.js +0 -1
- package/dist/daemon/commands.d.ts +0 -49
- package/dist/daemon/commands.js +0 -341
- package/dist/daemon/jsonRpcClient.d.ts +0 -38
- package/dist/daemon/jsonRpcClient.js +0 -129
- package/dist/daemon/socketClient.d.ts +0 -13
- package/dist/daemon/socketClient.js +0 -26
- package/dist/daemon-cli.d.ts +0 -10
- package/dist/daemon-cli.js +0 -15
- package/dist/hooks/useInputManager.d.ts +0 -75
- package/dist/hooks/useInputManager.js +0 -583
- package/dist/hooks/usePluginManager.d.ts +0 -4
- package/dist/hooks/usePluginManager.js +0 -265
- package/dist/hooks/useTasks.d.ts +0 -1
- package/dist/hooks/useTasks.js +0 -5
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -447
- package/dist/managers/inputHandlers.d.ts +0 -30
- package/dist/managers/inputHandlers.js +0 -625
- package/dist/managers/inputReducer.d.ts +0 -271
- package/dist/managers/inputReducer.js +0 -1121
- package/dist/plugin-manager-cli.d.ts +0 -5
- package/dist/plugin-manager-cli.js +0 -12
- package/dist/print-cli.d.ts +0 -10
- package/dist/print-cli.js +0 -209
- package/dist/reducers/agentsManagerReducer.d.ts +0 -26
- package/dist/reducers/agentsManagerReducer.js +0 -54
- package/dist/reducers/backgroundTaskManagerReducer.d.ts +0 -62
- package/dist/reducers/backgroundTaskManagerReducer.js +0 -106
- package/dist/reducers/confirmationReducer.d.ts +0 -37
- package/dist/reducers/confirmationReducer.js +0 -193
- package/dist/reducers/helpSelectorReducer.d.ts +0 -25
- package/dist/reducers/helpSelectorReducer.js +0 -52
- package/dist/reducers/marketplaceAddFormReducer.d.ts +0 -24
- package/dist/reducers/marketplaceAddFormReducer.js +0 -58
- package/dist/reducers/mcpManagerReducer.d.ts +0 -37
- package/dist/reducers/mcpManagerReducer.js +0 -74
- package/dist/reducers/pluginDetailReducer.d.ts +0 -32
- package/dist/reducers/pluginDetailReducer.js +0 -70
- package/dist/reducers/questionReducer.d.ts +0 -82
- package/dist/reducers/questionReducer.js +0 -375
- package/dist/reducers/rewindSelectorReducer.d.ts +0 -21
- package/dist/reducers/rewindSelectorReducer.js +0 -37
- package/dist/reducers/selectorReducer.d.ts +0 -23
- package/dist/reducers/selectorReducer.js +0 -42
- package/dist/reducers/workflowManagerReducer.d.ts +0 -40
- package/dist/reducers/workflowManagerReducer.js +0 -100
- package/dist/session-selector-cli.d.ts +0 -3
- package/dist/session-selector-cli.js +0 -25
- package/dist/stdio/agentBridge.d.ts +0 -139
- package/dist/stdio/agentBridge.js +0 -999
- package/dist/stdio/daemonServer.d.ts +0 -67
- package/dist/stdio/daemonServer.js +0 -191
- package/dist/stdio/index.d.ts +0 -5
- package/dist/stdio/index.js +0 -5
- package/dist/stdio/jsonRpcConnection.d.ts +0 -39
- package/dist/stdio/jsonRpcConnection.js +0 -135
- package/dist/stdio/protocol.d.ts +0 -41
- package/dist/stdio/protocol.js +0 -26
- package/dist/stdio/stdioServer.d.ts +0 -36
- package/dist/stdio/stdioServer.js +0 -51
- package/dist/stdio-cli.d.ts +0 -8
- package/dist/stdio-cli.js +0 -15
- package/dist/types.d.ts +0 -20
- package/dist/types.js +0 -1
- package/dist/utils/bracketedPaste.d.ts +0 -39
- package/dist/utils/bracketedPaste.js +0 -122
- package/dist/utils/clipboard.d.ts +0 -21
- package/dist/utils/clipboard.js +0 -347
- package/dist/utils/constants.d.ts +0 -16
- package/dist/utils/constants.js +0 -18
- package/dist/utils/highlightUtils.d.ts +0 -1
- package/dist/utils/highlightUtils.js +0 -93
- package/dist/utils/inputUtils.d.ts +0 -17
- package/dist/utils/inputUtils.js +0 -67
- package/dist/utils/logger.d.ts +0 -71
- package/dist/utils/logger.js +0 -259
- package/dist/utils/markdownTable.d.ts +0 -34
- package/dist/utils/markdownTable.js +0 -302
- package/dist/utils/rewindCheckpoints.d.ts +0 -8
- package/dist/utils/rewindCheckpoints.js +0 -17
- package/dist/utils/throttle.d.ts +0 -14
- package/dist/utils/throttle.js +0 -55
- package/dist/utils/toolParameterTransforms.d.ts +0 -18
- package/dist/utils/toolParameterTransforms.js +0 -73
- package/dist/utils/usageSummary.d.ts +0 -28
- package/dist/utils/usageSummary.js +0 -121
- package/dist/utils/version.d.ts +0 -1
- package/dist/utils/version.js +0 -4
- package/dist/utils/worktree.d.ts +0 -35
- package/dist/utils/worktree.js +0 -219
package/src/contexts/useChat.tsx
CHANGED
|
@@ -5,7 +5,6 @@ import React, {
|
|
|
5
5
|
useRef,
|
|
6
6
|
useEffect,
|
|
7
7
|
useState,
|
|
8
|
-
useMemo,
|
|
9
8
|
} from "react";
|
|
10
9
|
import { useInput, useStdout } from "ink";
|
|
11
10
|
import { useAppConfig } from "./useAppConfig.js";
|
|
@@ -42,6 +41,9 @@ export interface ChatContextType {
|
|
|
42
41
|
isLoading: boolean;
|
|
43
42
|
isCommandRunning: boolean;
|
|
44
43
|
isCompacting: boolean;
|
|
44
|
+
/** Accumulated streaming text from the compaction fork, shown as the
|
|
45
|
+
* last-30-chars tail on the CLI compacting loading indicator. */
|
|
46
|
+
compactionStream: string;
|
|
45
47
|
// Message display state
|
|
46
48
|
isExpanded: boolean;
|
|
47
49
|
isTaskListVisible: boolean;
|
|
@@ -144,8 +146,6 @@ export interface ChatContextType {
|
|
|
144
146
|
workdir?: string;
|
|
145
147
|
// Agent recreation (e.g. after plugin install)
|
|
146
148
|
recreateAgent: () => void;
|
|
147
|
-
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
148
|
-
triggerWorktreeRemoveHook: (worktreePath: string) => Promise<void>;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -178,177 +178,21 @@ interface StreamingUpdateParams {
|
|
|
178
178
|
* reasoning and text content alike. See docs/specs/core/stream-content-updates.md.
|
|
179
179
|
* The clone must be at least one layer deep (message + blocks) so the in-place
|
|
180
180
|
* block mutation never leaks into consumer state.
|
|
181
|
+
*
|
|
182
|
+
* IMPORTANT: the caller must invoke this EAGERLY at callback time and capture
|
|
183
|
+
* the result before scheduling any state update. React batches setState calls
|
|
184
|
+
* that happen in the same synchronous tick and runs the updater functions only
|
|
185
|
+
* at flush time — after the SDK has already written the first chunk into the
|
|
186
|
+
* shared message. A snapshot evaluated inside the updater (lazily) would copy
|
|
187
|
+
* the post-mutation blocks, and the first delta append would then double-count
|
|
188
|
+
* the first word ("HelloHello") whenever `onAssistantMessageAdded` and the
|
|
189
|
+
* first delta callback land in the same batch.
|
|
181
190
|
*/
|
|
182
191
|
const snapshotMessage = (message: Message): Message => ({
|
|
183
192
|
...message,
|
|
184
193
|
blocks: message.blocks.map((block) => ({ ...block })),
|
|
185
194
|
});
|
|
186
195
|
|
|
187
|
-
/**
|
|
188
|
-
* Window-concat throttle for pure-delta streaming updates: chunks arriving
|
|
189
|
-
* within the cooldown window are merged so no delta is lost (a dropped delta
|
|
190
|
-
* would permanently lose content, unlike the accumulated-payload throttle it
|
|
191
|
-
* replaces). Leading edge fires immediately; the trailing edge carries only
|
|
192
|
-
* chunks that arrived within the window. `end` flushes any pending deltas
|
|
193
|
-
* first, then forwards the end signal right away.
|
|
194
|
-
*/
|
|
195
|
-
function createStreamingWindowThrottle(
|
|
196
|
-
fn: (params: StreamingUpdateParams) => void,
|
|
197
|
-
wait: number,
|
|
198
|
-
): {
|
|
199
|
-
(params: StreamingUpdateParams): void;
|
|
200
|
-
cancel: () => void;
|
|
201
|
-
flush: () => void;
|
|
202
|
-
} {
|
|
203
|
-
let timer: NodeJS.Timeout | null = null;
|
|
204
|
-
let pending: { messageId: string; chunk: string } | null = null;
|
|
205
|
-
|
|
206
|
-
const fire = (stage: "streaming" | "end") => {
|
|
207
|
-
if (pending) {
|
|
208
|
-
fn({ ...pending, stage });
|
|
209
|
-
pending = null;
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const throttled = (params: StreamingUpdateParams) => {
|
|
214
|
-
if (params.stage === "end") {
|
|
215
|
-
// Flush any deltas still pending inside the cooldown window first
|
|
216
|
-
if (timer) {
|
|
217
|
-
clearTimeout(timer);
|
|
218
|
-
timer = null;
|
|
219
|
-
}
|
|
220
|
-
fire("streaming");
|
|
221
|
-
fn(params);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
if (pending) {
|
|
225
|
-
pending.chunk += params.chunk;
|
|
226
|
-
} else {
|
|
227
|
-
pending = { messageId: params.messageId, chunk: params.chunk };
|
|
228
|
-
}
|
|
229
|
-
if (!timer) {
|
|
230
|
-
// Leading edge: fire the current delta immediately, then reset pending so
|
|
231
|
-
// the trailing edge only carries chunks arriving within this window
|
|
232
|
-
fire("streaming");
|
|
233
|
-
timer = setTimeout(() => {
|
|
234
|
-
timer = null;
|
|
235
|
-
fire("streaming");
|
|
236
|
-
}, wait);
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
throttled.cancel = () => {
|
|
241
|
-
if (timer) {
|
|
242
|
-
clearTimeout(timer);
|
|
243
|
-
timer = null;
|
|
244
|
-
}
|
|
245
|
-
pending = null;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
throttled.flush = () => {
|
|
249
|
-
if (timer) {
|
|
250
|
-
clearTimeout(timer);
|
|
251
|
-
timer = null;
|
|
252
|
-
}
|
|
253
|
-
fire("streaming");
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
return throttled;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Per-tool window-concat throttle for pure-delta tool parameter streaming:
|
|
261
|
-
* `parametersChunk` deltas are accumulated independently per tool block id
|
|
262
|
-
* within the cooldown window, so interleaved multi-tool streams lose no delta
|
|
263
|
-
* (a plain throttle's single last-args slot would drop every earlier tool's
|
|
264
|
-
* deltas, leaving the first tool without streaming parameters). `start` /
|
|
265
|
-
* `running` apply immediately (one-shot snapshots); `end` flushes pending
|
|
266
|
-
* streaming deltas first, then applies the authoritative parameters/result.
|
|
267
|
-
*/
|
|
268
|
-
export function createToolStreamingThrottle(
|
|
269
|
-
fn: (params: ToolBlockUpdateCallbackParams) => void,
|
|
270
|
-
wait: number,
|
|
271
|
-
): {
|
|
272
|
-
(params: ToolBlockUpdateCallbackParams): void;
|
|
273
|
-
cancel: () => void;
|
|
274
|
-
flush: () => void;
|
|
275
|
-
} {
|
|
276
|
-
let timer: NodeJS.Timeout | null = null;
|
|
277
|
-
let pending: { messageId: string; chunks: Map<string, string> } | null = null;
|
|
278
|
-
|
|
279
|
-
const fire = () => {
|
|
280
|
-
if (pending && pending.chunks.size > 0) {
|
|
281
|
-
const { messageId, chunks } = pending;
|
|
282
|
-
pending = null;
|
|
283
|
-
for (const [id, chunk] of chunks) {
|
|
284
|
-
fn({ messageId, id, parametersChunk: chunk, stage: "streaming" });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
const throttled = (params: ToolBlockUpdateCallbackParams) => {
|
|
290
|
-
if (params.stage === "end") {
|
|
291
|
-
// Flush any deltas still pending inside the cooldown window first
|
|
292
|
-
if (timer) {
|
|
293
|
-
clearTimeout(timer);
|
|
294
|
-
timer = null;
|
|
295
|
-
}
|
|
296
|
-
fire();
|
|
297
|
-
fn(params);
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
if (params.stage === "streaming") {
|
|
301
|
-
if (!pending) {
|
|
302
|
-
pending = { messageId: params.messageId, chunks: new Map() };
|
|
303
|
-
}
|
|
304
|
-
const prev = pending.chunks.get(params.id) || "";
|
|
305
|
-
pending.chunks.set(params.id, prev + (params.parametersChunk || ""));
|
|
306
|
-
if (!timer) {
|
|
307
|
-
timer = setTimeout(() => {
|
|
308
|
-
timer = null;
|
|
309
|
-
fire();
|
|
310
|
-
}, wait);
|
|
311
|
-
}
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
// start / running — one-shot snapshots applied immediately. Drop this
|
|
315
|
-
// tool's buffered streaming deltas first: start/running carry the
|
|
316
|
-
// authoritative parameters, and a pending timer would otherwise fire late
|
|
317
|
-
// with a stale `streaming` event, regressing this tool block's stage back
|
|
318
|
-
// to streaming (yellow dot -> gray) mid-execution. Other tools' in-flight
|
|
319
|
-
// chunks are kept so interleaved multi-tool streaming still accumulates.
|
|
320
|
-
if (pending) {
|
|
321
|
-
pending.chunks.delete(params.id);
|
|
322
|
-
if (pending.chunks.size === 0) {
|
|
323
|
-
pending = null;
|
|
324
|
-
if (timer) {
|
|
325
|
-
clearTimeout(timer);
|
|
326
|
-
timer = null;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
fn(params);
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
throttled.cancel = () => {
|
|
334
|
-
if (timer) {
|
|
335
|
-
clearTimeout(timer);
|
|
336
|
-
timer = null;
|
|
337
|
-
}
|
|
338
|
-
pending = null;
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
throttled.flush = () => {
|
|
342
|
-
if (timer) {
|
|
343
|
-
clearTimeout(timer);
|
|
344
|
-
timer = null;
|
|
345
|
-
}
|
|
346
|
-
fire();
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
return throttled;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
196
|
export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
353
197
|
children,
|
|
354
198
|
bypassPermissions,
|
|
@@ -379,157 +223,138 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
379
223
|
const [latestTotalTokens, setLatestTotalTokens] = useState(0);
|
|
380
224
|
const [maxInputTokens, setMaxInputTokens] = useState(200000);
|
|
381
225
|
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
);
|
|
226
|
+
// Direct incremental streaming updaters — every delta is applied to the
|
|
227
|
+
// message state immediately, without a cooldown window. The former 500ms
|
|
228
|
+
// window-concat throttle is gone: its trailing-edge flush could interleave
|
|
229
|
+
// with a full-list `refreshMessages` snapshot replacement mid-stream and
|
|
230
|
+
// re-append old deltas on top of the authoritative content (first-word
|
|
231
|
+
// duplication). Terminal write frequency is bounded downstream by Ink's
|
|
232
|
+
// built-in ~30fps output throttle. See
|
|
233
|
+
// docs/specs/core/stream-content-updates.md.
|
|
234
|
+
const applyContentUpdate = useCallback((params: StreamingUpdateParams) => {
|
|
235
|
+
const { messageId, chunk, stage } = params;
|
|
236
|
+
setMessages((prev) =>
|
|
237
|
+
prev.map((m) => {
|
|
238
|
+
if (m.id !== messageId) return m;
|
|
239
|
+
const textBlockIndex = m.blocks.findIndex((b) => b.type === "text");
|
|
240
|
+
if (textBlockIndex === -1) {
|
|
241
|
+
return {
|
|
242
|
+
...m,
|
|
243
|
+
blocks: [...m.blocks, { type: "text", content: chunk, stage }],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
...m,
|
|
248
|
+
blocks: m.blocks.map((b, idx) =>
|
|
249
|
+
idx === textBlockIndex && b.type === "text"
|
|
250
|
+
? {
|
|
251
|
+
...b,
|
|
252
|
+
content: (b.content || "") + chunk,
|
|
253
|
+
stage,
|
|
254
|
+
}
|
|
255
|
+
: b,
|
|
256
|
+
),
|
|
257
|
+
};
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
}, []);
|
|
418
261
|
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const reasoningBlockIndex = m.blocks.findIndex(
|
|
427
|
-
(b) => b.type === "reasoning",
|
|
428
|
-
);
|
|
429
|
-
if (reasoningBlockIndex === -1) {
|
|
430
|
-
return {
|
|
431
|
-
...m,
|
|
432
|
-
blocks: [
|
|
433
|
-
...m.blocks,
|
|
434
|
-
{ type: "reasoning", content: chunk, stage },
|
|
435
|
-
],
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
...m,
|
|
440
|
-
blocks: m.blocks.map((b, idx) =>
|
|
441
|
-
idx === reasoningBlockIndex && b.type === "reasoning"
|
|
442
|
-
? {
|
|
443
|
-
...b,
|
|
444
|
-
content: (b.content || "") + chunk,
|
|
445
|
-
stage,
|
|
446
|
-
}
|
|
447
|
-
: b,
|
|
448
|
-
),
|
|
449
|
-
};
|
|
450
|
-
}),
|
|
262
|
+
const applyReasoningUpdate = useCallback((params: StreamingUpdateParams) => {
|
|
263
|
+
const { messageId, chunk, stage } = params;
|
|
264
|
+
setMessages((prev) =>
|
|
265
|
+
prev.map((m) => {
|
|
266
|
+
if (m.id !== messageId) return m;
|
|
267
|
+
const reasoningBlockIndex = m.blocks.findIndex(
|
|
268
|
+
(b) => b.type === "reasoning",
|
|
451
269
|
);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
270
|
+
if (reasoningBlockIndex === -1) {
|
|
271
|
+
return {
|
|
272
|
+
...m,
|
|
273
|
+
blocks: [...m.blocks, { type: "reasoning", content: chunk, stage }],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
...m,
|
|
278
|
+
blocks: m.blocks.map((b, idx) =>
|
|
279
|
+
idx === reasoningBlockIndex && b.type === "reasoning"
|
|
280
|
+
? {
|
|
281
|
+
...b,
|
|
282
|
+
content: (b.content || "") + chunk,
|
|
283
|
+
stage,
|
|
284
|
+
}
|
|
285
|
+
: b,
|
|
286
|
+
),
|
|
287
|
+
};
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
}, []);
|
|
455
291
|
|
|
456
|
-
const
|
|
457
|
-
() =>
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (toolBlockIndex === -1) {
|
|
472
|
-
return {
|
|
473
|
-
...m,
|
|
474
|
-
blocks: [
|
|
475
|
-
...m.blocks,
|
|
476
|
-
{
|
|
477
|
-
type: "tool",
|
|
478
|
-
id: toolBlockId,
|
|
479
|
-
name: updates.name || "",
|
|
480
|
-
stage: updates.stage || "start",
|
|
481
|
-
parameters:
|
|
482
|
-
(updates.parameters || "") + (parametersChunk || ""),
|
|
483
|
-
result: updates.result || "",
|
|
484
|
-
...updates,
|
|
485
|
-
},
|
|
486
|
-
],
|
|
487
|
-
};
|
|
488
|
-
}
|
|
292
|
+
const applyToolBlockUpdate = useCallback(
|
|
293
|
+
(params: ToolBlockUpdateCallbackParams) => {
|
|
294
|
+
const {
|
|
295
|
+
messageId,
|
|
296
|
+
id: toolBlockId,
|
|
297
|
+
parametersChunk,
|
|
298
|
+
...updates
|
|
299
|
+
} = params;
|
|
300
|
+
setMessages((prev) =>
|
|
301
|
+
prev.map((m) => {
|
|
302
|
+
if (m.id !== messageId) return m;
|
|
303
|
+
const toolBlockIndex = m.blocks.findIndex(
|
|
304
|
+
(b) => b.type === "tool" && b.id === toolBlockId,
|
|
305
|
+
);
|
|
306
|
+
if (toolBlockIndex === -1) {
|
|
489
307
|
return {
|
|
490
308
|
...m,
|
|
491
|
-
blocks:
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
: b,
|
|
506
|
-
),
|
|
309
|
+
blocks: [
|
|
310
|
+
...m.blocks,
|
|
311
|
+
{
|
|
312
|
+
type: "tool",
|
|
313
|
+
id: toolBlockId,
|
|
314
|
+
name: updates.name || "",
|
|
315
|
+
stage: updates.stage || "start",
|
|
316
|
+
parameters:
|
|
317
|
+
(updates.parameters || "") + (parametersChunk || ""),
|
|
318
|
+
result: updates.result || "",
|
|
319
|
+
...updates,
|
|
320
|
+
},
|
|
321
|
+
],
|
|
507
322
|
};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
...m,
|
|
326
|
+
blocks: m.blocks.map((b, idx) =>
|
|
327
|
+
idx === toolBlockIndex && b.type === "tool"
|
|
328
|
+
? {
|
|
329
|
+
...b,
|
|
330
|
+
...updates,
|
|
331
|
+
// Streaming carries only the delta; append it to the
|
|
332
|
+
// accumulated parameters. start/running/end carry the
|
|
333
|
+
// authoritative value and replace wholesale.
|
|
334
|
+
parameters: parametersChunk
|
|
335
|
+
? (b.parameters || "") + parametersChunk
|
|
336
|
+
: updates.parameters !== undefined
|
|
337
|
+
? updates.parameters
|
|
338
|
+
: b.parameters,
|
|
339
|
+
}
|
|
340
|
+
: b,
|
|
341
|
+
),
|
|
342
|
+
};
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
},
|
|
511
346
|
[],
|
|
512
347
|
);
|
|
513
348
|
|
|
514
349
|
useEffect(() => {
|
|
515
350
|
isExpandedRef.current = isExpanded;
|
|
516
|
-
|
|
517
|
-
// Cancel pending throttled updates so the frozen expanded view isn't overwritten
|
|
518
|
-
throttledContentUpdate.cancel();
|
|
519
|
-
throttledReasoningUpdate.cancel();
|
|
520
|
-
throttledToolBlockUpdate.cancel();
|
|
521
|
-
}
|
|
522
|
-
}, [
|
|
523
|
-
isExpanded,
|
|
524
|
-
throttledContentUpdate,
|
|
525
|
-
throttledReasoningUpdate,
|
|
526
|
-
throttledToolBlockUpdate,
|
|
527
|
-
]);
|
|
351
|
+
}, [isExpanded]);
|
|
528
352
|
|
|
529
353
|
const [isLoading, setIsLoading] = useState(false);
|
|
530
354
|
const [sessionId, setSessionId] = useState("");
|
|
531
355
|
const [isCommandRunning, setIsCommandRunning] = useState(false);
|
|
532
356
|
const [isCompacting, setIsCompacting] = useState(false);
|
|
357
|
+
const [compactionStream, setCompactionStream] = useState("");
|
|
533
358
|
const [currentModel, setCurrentModelState] = useState("");
|
|
534
359
|
const [configuredModels, setConfiguredModels] = useState<string[]>([]);
|
|
535
360
|
const [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);
|
|
@@ -603,8 +428,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
603
428
|
const [remountKey, setRemountKey] = useState(0);
|
|
604
429
|
|
|
605
430
|
// Full terminal clear + remount so Ink's append-only <Static> re-renders.
|
|
606
|
-
// Used on structural actions (/clear, /compact, rewind, ctrl-o
|
|
607
|
-
//
|
|
431
|
+
// Used on structural actions (/clear, /compact, rewind, ctrl-o) where stale
|
|
432
|
+
// Static output must not linger on screen.
|
|
608
433
|
const forceRemount = useCallback(() => {
|
|
609
434
|
stdout?.write("\u001b[2J\u001b[3J\u001b[0;0H", () => {
|
|
610
435
|
setRemountKey((prev) => prev + 1);
|
|
@@ -671,34 +496,37 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
671
496
|
const msgs = agentRef.current.messages;
|
|
672
497
|
const last = msgs[msgs.length - 1];
|
|
673
498
|
if (!last || last.role !== "user") return;
|
|
499
|
+
// Eager snapshot at callback time — React defers updater execution to
|
|
500
|
+
// the batch flush, so evaluating snapshotMessage inside setMessages
|
|
501
|
+
// would read the SDK message after its in-place mutations.
|
|
502
|
+
const snapshot = snapshotMessage(last);
|
|
674
503
|
setMessages((prev) =>
|
|
675
|
-
prev.some((m) => m.id === last.id)
|
|
676
|
-
? prev
|
|
677
|
-
: [...prev, snapshotMessage(last)],
|
|
504
|
+
prev.some((m) => m.id === last.id) ? prev : [...prev, snapshot],
|
|
678
505
|
);
|
|
679
506
|
},
|
|
680
507
|
onAssistantMessageAdded: (messageId: string) => {
|
|
681
508
|
if (isExpandedRef.current || !agentRef.current) return;
|
|
682
509
|
const msg = agentRef.current.messages.find((m) => m.id === messageId);
|
|
683
510
|
if (!msg) return;
|
|
511
|
+
// Eager snapshot (see onUserMessageAdded): `addAssistantMessage()`
|
|
512
|
+
// fires this callback BEFORE the first delta writes into the shared
|
|
513
|
+
// message, so capturing here copies the pre-mutation (empty) blocks.
|
|
514
|
+
const snapshot = snapshotMessage(msg);
|
|
684
515
|
setMessages((prev) =>
|
|
685
|
-
prev.some((m) => m.id === messageId)
|
|
686
|
-
? prev
|
|
687
|
-
: [...prev, snapshotMessage(msg)],
|
|
516
|
+
prev.some((m) => m.id === messageId) ? prev : [...prev, snapshot],
|
|
688
517
|
);
|
|
689
518
|
},
|
|
690
519
|
onAssistantContentUpdated: (params) => {
|
|
691
520
|
if (isExpandedRef.current) return;
|
|
692
|
-
|
|
521
|
+
applyContentUpdate(params);
|
|
693
522
|
},
|
|
694
523
|
onAssistantReasoningUpdated: (params) => {
|
|
695
524
|
if (isExpandedRef.current) return;
|
|
696
|
-
|
|
525
|
+
applyReasoningUpdate(params);
|
|
697
526
|
},
|
|
698
527
|
onToolBlockUpdated: (params) => {
|
|
699
528
|
if (isExpandedRef.current) return;
|
|
700
|
-
|
|
701
|
-
if (params.stage === "end") throttledToolBlockUpdate.flush();
|
|
529
|
+
applyToolBlockUpdate(params);
|
|
702
530
|
},
|
|
703
531
|
onErrorBlockAdded: (error: string) => {
|
|
704
532
|
if (isExpandedRef.current) return;
|
|
@@ -806,6 +634,14 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
806
634
|
},
|
|
807
635
|
onCompactionStateChange: (isCompactingState) => {
|
|
808
636
|
setIsCompacting(isCompactingState);
|
|
637
|
+
if (!isCompactingState) {
|
|
638
|
+
setCompactionStream("");
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
onCompactionContentUpdate: (content) => {
|
|
642
|
+
// The SDK delivers the accumulated compaction text; the loading
|
|
643
|
+
// indicator renders only its last 30 characters (streaming tail).
|
|
644
|
+
setCompactionStream(content);
|
|
809
645
|
},
|
|
810
646
|
onBackgroundTasksChange: async (tasks) => {
|
|
811
647
|
setBackgroundTasks([...tasks]);
|
|
@@ -955,9 +791,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
955
791
|
originalCwd,
|
|
956
792
|
model,
|
|
957
793
|
initialPermissionMode,
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
794
|
+
applyContentUpdate,
|
|
795
|
+
applyReasoningUpdate,
|
|
796
|
+
applyToolBlockUpdate,
|
|
961
797
|
mcpServers,
|
|
962
798
|
],
|
|
963
799
|
);
|
|
@@ -996,9 +832,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
996
832
|
// Cleanup on unmount
|
|
997
833
|
useEffect(() => {
|
|
998
834
|
return () => {
|
|
999
|
-
throttledContentUpdate.cancel();
|
|
1000
|
-
throttledReasoningUpdate.cancel();
|
|
1001
|
-
throttledToolBlockUpdate.cancel();
|
|
1002
835
|
if (agentRef.current) {
|
|
1003
836
|
try {
|
|
1004
837
|
// Display usage summary before cleanup
|
|
@@ -1012,19 +845,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
1012
845
|
agentRef.current.destroy();
|
|
1013
846
|
}
|
|
1014
847
|
};
|
|
1015
|
-
}, [
|
|
1016
|
-
throttledContentUpdate,
|
|
1017
|
-
throttledReasoningUpdate,
|
|
1018
|
-
throttledToolBlockUpdate,
|
|
1019
|
-
]);
|
|
1020
|
-
|
|
1021
|
-
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
1022
|
-
const triggerWorktreeRemoveHook = useCallback(
|
|
1023
|
-
async (worktreePath: string) => {
|
|
1024
|
-
await agentRef.current?.triggerWorktreeRemoveHook(worktreePath);
|
|
1025
|
-
},
|
|
1026
|
-
[],
|
|
1027
|
-
);
|
|
848
|
+
}, []);
|
|
1028
849
|
|
|
1029
850
|
// Send message function (including judgment logic)
|
|
1030
851
|
const sendMessage = useCallback(
|
|
@@ -1354,6 +1175,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
1354
1175
|
getConfiguredModels,
|
|
1355
1176
|
setModel,
|
|
1356
1177
|
isCompacting,
|
|
1178
|
+
compactionStream,
|
|
1357
1179
|
mcpServers: mcpServerStatuses,
|
|
1358
1180
|
connectMcpServer,
|
|
1359
1181
|
disconnectMcpServer,
|
|
@@ -1387,7 +1209,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
1387
1209
|
version,
|
|
1388
1210
|
workdir,
|
|
1389
1211
|
recreateAgent,
|
|
1390
|
-
triggerWorktreeRemoveHook,
|
|
1391
1212
|
};
|
|
1392
1213
|
|
|
1393
1214
|
return (
|