@hienlh/ppm 0.11.3 → 0.11.5
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/CHANGELOG.md +12 -0
- package/dist/web/assets/{chat-tab-1Khvyjvx.js → chat-tab-CWc9ZYVY.js} +6 -6
- package/dist/web/assets/{code-editor-BtOD0A1r.js → code-editor-CSej2S2J.js} +2 -2
- package/dist/web/assets/{conflict-editor-DsLm2d9j.js → conflict-editor-BmuhLalI.js} +1 -1
- package/dist/web/assets/{database-viewer-Bf-FjggD.js → database-viewer-CqW0fH7Q.js} +1 -1
- package/dist/web/assets/{diff-viewer-DZcLi76X.js → diff-viewer-ium8zJhL.js} +1 -1
- package/dist/web/assets/{extension-webview-Dt50AKjl.js → extension-webview-AxcQX8ML.js} +1 -1
- package/dist/web/assets/{index-CwVBevJP.js → index-CyciQ-Gz.js} +3 -3
- package/dist/web/assets/{markdown-renderer-INc5L7kL.js → markdown-renderer-Ckjp96ej.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-Ds8rV7YG.js → port-forwarding-tab-DR5Bngg2.js} +1 -1
- package/dist/web/assets/{postgres-viewer-Cvedgnv5.js → postgres-viewer-hy2EksiB.js} +1 -1
- package/dist/web/assets/{settings-tab-D6jFbyjn.js → settings-tab-D5SXI74b.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CXbf9WUf.js → sqlite-viewer-DDPZjt-X.js} +1 -1
- package/dist/web/assets/{terminal-tab-CGu6B-nW.js → terminal-tab-DSWdc4AO.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/server/ws/chat.ts +23 -1
- package/src/web/components/chat/tool-cards.tsx +2 -1
- package/src/web/components/explorer/file-tree.tsx +3 -2
- package/src/web/hooks/use-chat.ts +43 -4
|
@@ -88,6 +88,8 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
88
88
|
const pendingMessageRef = useRef<string | null>(null);
|
|
89
89
|
const sendRef = useRef<(data: string) => void>(() => {});
|
|
90
90
|
const refetchRef = useRef<(() => void) | null>(null);
|
|
91
|
+
/** True while replaying turn_events — suppresses setPendingApproval */
|
|
92
|
+
const isReplayingRef = useRef(false);
|
|
91
93
|
const sessionIdRef = useRef(sessionId);
|
|
92
94
|
sessionIdRef.current = sessionId;
|
|
93
95
|
const projectNameRef = useRef(projectName);
|
|
@@ -251,6 +253,9 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
251
253
|
|
|
252
254
|
case "approval_request": {
|
|
253
255
|
streamingEventsRef.current.push(ev as ChatEvent);
|
|
256
|
+
// During turn_events replay, session_state already set the correct
|
|
257
|
+
// pendingApproval — skip re-setting it for historical (already-answered) events
|
|
258
|
+
if (isReplayingRef.current) break;
|
|
254
259
|
setPendingApproval({
|
|
255
260
|
requestId: ev.requestId,
|
|
256
261
|
tool: ev.tool,
|
|
@@ -361,6 +366,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
361
366
|
// Finalize the streaming message — preserve SDK UUID for fork/rewind
|
|
362
367
|
const finalContent = streamingContentRef.current;
|
|
363
368
|
const finalEvents = [...streamingEventsRef.current];
|
|
369
|
+
const finalAccount = streamingAccountRef.current;
|
|
364
370
|
const doneUuid = ev.lastMessageUuid as string | undefined;
|
|
365
371
|
setMessages((prev) => {
|
|
366
372
|
const last = prev[prev.length - 1];
|
|
@@ -373,6 +379,19 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
373
379
|
...(doneUuid && { sdkUuid: doneUuid }),
|
|
374
380
|
}];
|
|
375
381
|
}
|
|
382
|
+
// No assistant message flushed yet (rAF was still pending when cancelled).
|
|
383
|
+
// Create one from accumulated refs so the response isn't silently lost.
|
|
384
|
+
if (finalContent || finalEvents.length > 0) {
|
|
385
|
+
return [...prev, {
|
|
386
|
+
id: `final-${Date.now()}`,
|
|
387
|
+
role: "assistant" as const,
|
|
388
|
+
content: finalContent,
|
|
389
|
+
events: finalEvents,
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
...(doneUuid && { sdkUuid: doneUuid }),
|
|
392
|
+
...finalAccount,
|
|
393
|
+
}];
|
|
394
|
+
}
|
|
376
395
|
return prev;
|
|
377
396
|
});
|
|
378
397
|
streamingContentRef.current = "";
|
|
@@ -457,12 +476,30 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
457
476
|
// Handle turn_events (reconnect sync with rAF chunking)
|
|
458
477
|
if ((data as any).type === "turn_events") {
|
|
459
478
|
const events = (data as any).events as unknown[];
|
|
460
|
-
|
|
479
|
+
const userMessage = (data as any).userMessage as string | null;
|
|
480
|
+
if (!events?.length && !userMessage) { setIsReconnecting(false); return; }
|
|
461
481
|
|
|
462
|
-
//
|
|
482
|
+
// Remove stale streaming assistant message + inject current turn's user message
|
|
463
483
|
setMessages(prev => {
|
|
464
|
-
|
|
465
|
-
|
|
484
|
+
let updated = prev;
|
|
485
|
+
// Only remove in-progress streaming assistant (not finalized or REST-loaded)
|
|
486
|
+
const last = updated[updated.length - 1];
|
|
487
|
+
if (last?.role === "assistant" && last.id.startsWith("streaming-")) {
|
|
488
|
+
updated = updated.slice(0, -1);
|
|
489
|
+
}
|
|
490
|
+
// Add the current turn's user message if not already present
|
|
491
|
+
if (userMessage) {
|
|
492
|
+
const lastAfter = updated[updated.length - 1];
|
|
493
|
+
if (lastAfter?.role !== "user" || lastAfter.content !== userMessage) {
|
|
494
|
+
updated = [...updated, {
|
|
495
|
+
id: `user-replay-${Date.now()}`,
|
|
496
|
+
role: "user" as const,
|
|
497
|
+
content: userMessage,
|
|
498
|
+
timestamp: new Date().toISOString(),
|
|
499
|
+
}];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return updated;
|
|
466
503
|
});
|
|
467
504
|
|
|
468
505
|
// Reset streaming refs
|
|
@@ -471,6 +508,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
471
508
|
streamingAccountRef.current = null;
|
|
472
509
|
|
|
473
510
|
// Process events in chunks via requestAnimationFrame to avoid blocking main thread
|
|
511
|
+
isReplayingRef.current = true;
|
|
474
512
|
const CHUNK_SIZE = 100;
|
|
475
513
|
let offset = 0;
|
|
476
514
|
const processChunk = () => {
|
|
@@ -482,6 +520,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
482
520
|
if (offset < events.length) {
|
|
483
521
|
requestAnimationFrame(processChunk);
|
|
484
522
|
} else {
|
|
523
|
+
isReplayingRef.current = false;
|
|
485
524
|
setIsReconnecting(false);
|
|
486
525
|
}
|
|
487
526
|
};
|