@kenkaiiii/ggcoder 4.3.220 → 4.3.222
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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +4 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/ui/App.d.ts +4 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +46 -2
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +7 -1
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/chat-layout-pinning.test.js +20 -0
- package/dist/ui/chat-layout-pinning.test.js.map +1 -1
- package/dist/ui/components/AssistantMessage.test.js +36 -6
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/ChatLivePane.d.ts.map +1 -1
- package/dist/ui/components/ChatLivePane.js +1 -1
- package/dist/ui/components/ChatLivePane.js.map +1 -1
- package/dist/ui/components/SessionSummary.d.ts +5 -0
- package/dist/ui/components/SessionSummary.d.ts.map +1 -0
- package/dist/ui/components/SessionSummary.js +32 -0
- package/dist/ui/components/SessionSummary.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +9 -4
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/queued-message.test.js +55 -3
- package/dist/ui/queued-message.test.js.map +1 -1
- package/dist/ui/render.d.ts +2 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +5 -0
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/session-summary.d.ts +63 -0
- package/dist/ui/session-summary.d.ts.map +1 -0
- package/dist/ui/session-summary.js +81 -0
- package/dist/ui/session-summary.js.map +1 -0
- package/dist/ui/terminal-history-format.d.ts +1 -0
- package/dist/ui/terminal-history-format.d.ts.map +1 -1
- package/dist/ui/terminal-history-format.js +2 -1
- package/dist/ui/terminal-history-format.js.map +1 -1
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +45 -6
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +49 -1
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.js +3 -0
- package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
- package/dist/ui/transcript/spacing.d.ts +4 -0
- package/dist/ui/transcript/spacing.d.ts.map +1 -1
- package/dist/ui/transcript/spacing.js +17 -24
- package/dist/ui/transcript/spacing.js.map +1 -1
- package/dist/ui/transcript/spacing.test.js +137 -1
- package/dist/ui/transcript/spacing.test.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +39 -0
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.d.ts +1 -1
- package/dist/ui/utils/assistant-stream-split.d.ts.map +1 -1
- package/dist/ui/utils/assistant-stream-split.js +2 -35
- package/dist/ui/utils/assistant-stream-split.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.test.js +11 -52
- package/dist/ui/utils/assistant-stream-split.test.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.js +2 -2
- package/dist/ui/utils/markdown-renderer.js.map +1 -1
- package/package.json +4 -4
package/dist/ui/App.js
CHANGED
|
@@ -15,6 +15,7 @@ import { useTranscriptHistory } from "./hooks/useTranscriptHistory.js";
|
|
|
15
15
|
import { createWebSearchTool } from "../tools/web-search.js";
|
|
16
16
|
import { ChatScreen } from "./components/ChatScreen.js";
|
|
17
17
|
import { FullScreenOverlayRouter } from "./components/FullScreenOverlayRouter.js";
|
|
18
|
+
import { SessionSummaryDisplay } from "./components/SessionSummary.js";
|
|
18
19
|
import { reconcileGoalStatusEntriesWithRuns, removeGoalStatusEntry, syncGoalStatusEntries, } from "./components/GoalStatusBar.js";
|
|
19
20
|
import { useTheme, useSetTheme } from "./theme/theme.js";
|
|
20
21
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
@@ -57,6 +58,7 @@ import { renderTranscriptItem } from "./transcript/TranscriptRenderer.js";
|
|
|
57
58
|
import { formatDuration } from "./duration-format.js";
|
|
58
59
|
import { pickDurationVerb } from "./duration-summary.js";
|
|
59
60
|
import { toErrorItem } from "./error-item.js";
|
|
61
|
+
import { addLinesChanged, buildSessionSummary, createSessionStats, recordServerToolCall, recordToolEnd, recordTurnEnd, } from "./session-summary.js";
|
|
60
62
|
import { buildGoalDirtyWorktreePauseRun, buildGoalDirtyWorktreeUserPrompt, buildGoalTaskPromptWithReferences, buildGoalUserPauseRun, goalDirtyWorktreeInfoText, goalRunNeedsExplicitContinuationAfterWorker, goalTaskProgress, shouldKeepGoalRunTrackedAfterDecision, shouldRunGoalTaskInMainCheckout, } from "./goal-run-helpers.js";
|
|
61
63
|
import { compactHistory, getNextGeneratedItemId, isActiveItem, isSameAssistantText, normalizeAssistantText, partitionCompleted, pinStreamingTextBeforeToolBoundary, removeItemsWithIds, uniqueItemsById, } from "./item-helpers.js";
|
|
62
64
|
export { buildGoalSetupPromptFromPlanner, buildUserContentWithAttachments, collectAssistantTextSince, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
|
|
@@ -85,6 +87,7 @@ export function App(props) {
|
|
|
85
87
|
// Hoisted before terminal title hook so it can reference them
|
|
86
88
|
const [lastUserMessage, setLastUserMessage] = useState("");
|
|
87
89
|
const [exitPending, setExitPending] = useState(false);
|
|
90
|
+
const [quittingSummary, setQuittingSummary] = useState(null);
|
|
88
91
|
const [goalMode, setGoalMode] = useState(props.sessionStore?.goalMode ?? props.goalModeRef?.current ?? "off");
|
|
89
92
|
const [planMode, setPlanMode] = useState(props.sessionStore?.planMode ?? props.planModeRef?.current ?? false);
|
|
90
93
|
// Terminal title — updated later after agentLoop is created
|
|
@@ -180,6 +183,8 @@ export function App(props) {
|
|
|
180
183
|
const sessionManagerRef = useRef(props.sessionsDir ? new SessionManager(props.sessionsDir) : null);
|
|
181
184
|
const sessionPathRef = useRef(props.sessionStore?.sessionPath ?? props.sessionPath);
|
|
182
185
|
const persistedIndexRef = useRef(messagesRef.current.length);
|
|
186
|
+
const sessionStatsRef = useRef(props.sessionStore?.sessionStats ??
|
|
187
|
+
createSessionStats({ sessionId: props.sessionStore?.sessionId ?? props.sessionId }));
|
|
183
188
|
/** Last actual API-reported input token count (from turn_end). */
|
|
184
189
|
const lastActualTokensRef = useRef(0);
|
|
185
190
|
/** Timestamp (ms) when lastActualTokensRef was last updated by turn_end. */
|
|
@@ -317,6 +322,10 @@ export function App(props) {
|
|
|
317
322
|
if (sessionStore)
|
|
318
323
|
sessionStore.planMode = planMode;
|
|
319
324
|
}, [planMode, sessionStore]);
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (sessionStore)
|
|
327
|
+
sessionStore.sessionStats = sessionStatsRef.current;
|
|
328
|
+
}, [sessionStore]);
|
|
320
329
|
// pendingAction is consumed via a useEffect AFTER agentLoop is created
|
|
321
330
|
// — see below where useAgentLoop is set up.
|
|
322
331
|
const pendingActionConsumedRef = useRef(false);
|
|
@@ -565,9 +574,11 @@ export function App(props) {
|
|
|
565
574
|
messages: compactedMessages,
|
|
566
575
|
});
|
|
567
576
|
sessionPathRef.current = session.path;
|
|
577
|
+
sessionStatsRef.current.sessionId = session.id;
|
|
568
578
|
persistedIndexRef.current = compactedMessages.length;
|
|
569
579
|
if (sessionStore) {
|
|
570
580
|
sessionStore.sessionPath = session.path;
|
|
581
|
+
sessionStore.sessionId = session.id;
|
|
571
582
|
sessionStore.messages = [...compactedMessages];
|
|
572
583
|
}
|
|
573
584
|
log("INFO", "compaction", "Persisted compacted session checkpoint", { path: session.path });
|
|
@@ -582,6 +593,7 @@ export function App(props) {
|
|
|
582
593
|
if (sessionStore) {
|
|
583
594
|
sessionStore.messages = [...allMsgs];
|
|
584
595
|
sessionStore.sessionPath = sp;
|
|
596
|
+
sessionStore.sessionId = sessionStatsRef.current.sessionId;
|
|
585
597
|
}
|
|
586
598
|
}, [appendMessagesToSession, sessionStore]);
|
|
587
599
|
/**
|
|
@@ -1051,6 +1063,14 @@ export function App(props) {
|
|
|
1051
1063
|
});
|
|
1052
1064
|
}, []),
|
|
1053
1065
|
onToolEnd: useCallback((toolCallId, name, result, isError, durationMs, details) => {
|
|
1066
|
+
recordToolEnd(sessionStatsRef.current, name, isError, durationMs);
|
|
1067
|
+
if (name === "edit" && !isError) {
|
|
1068
|
+
const diff = details?.diff ?? result;
|
|
1069
|
+
addLinesChanged(sessionStatsRef.current, {
|
|
1070
|
+
added: (diff.match(/^\+[^+]/gm) ?? []).length,
|
|
1071
|
+
removed: (diff.match(/^-[^-]/gm) ?? []).length,
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1054
1074
|
// Language-pack detection — gated on `write`/`bash` inside the
|
|
1055
1075
|
// helper; cheap to call unconditionally. Fire-and-forget; the next
|
|
1056
1076
|
// LLM turn picks up the swapped system prompt automatically.
|
|
@@ -1158,6 +1178,7 @@ export function App(props) {
|
|
|
1158
1178
|
}
|
|
1159
1179
|
}, []),
|
|
1160
1180
|
onServerToolCall: useCallback((id, name, input, stream) => {
|
|
1181
|
+
recordServerToolCall(sessionStatsRef.current);
|
|
1161
1182
|
log("INFO", "server_tool", `Server tool call: ${name}`, { id });
|
|
1162
1183
|
const startedAt = Date.now();
|
|
1163
1184
|
const animateUntil = startedAt + RUNNING_INDICATOR_ANIMATION_MS;
|
|
@@ -1231,6 +1252,7 @@ export function App(props) {
|
|
|
1231
1252
|
});
|
|
1232
1253
|
}, [queueFlush]),
|
|
1233
1254
|
onTurnEnd: useCallback((turn, stopReason, usage) => {
|
|
1255
|
+
recordTurnEnd(sessionStatsRef.current, usage);
|
|
1234
1256
|
log("INFO", "turn", `Turn ${turn} ended`, {
|
|
1235
1257
|
stopReason,
|
|
1236
1258
|
inputTokens: String(usage.inputTokens),
|
|
@@ -1540,6 +1562,24 @@ export function App(props) {
|
|
|
1540
1562
|
return () => clearTimeout(timer);
|
|
1541
1563
|
}
|
|
1542
1564
|
}, [agentLoop.isRunning, sessionStore, props.resetUI]);
|
|
1565
|
+
const showSessionSummaryAndExit = useCallback(() => {
|
|
1566
|
+
const summary = buildSessionSummary({
|
|
1567
|
+
stats: sessionStatsRef.current,
|
|
1568
|
+
provider: currentProvider,
|
|
1569
|
+
model: currentModel,
|
|
1570
|
+
cwd: displayedCwd,
|
|
1571
|
+
footer: sessionStatsRef.current.sessionId
|
|
1572
|
+
? `To resume this session: ggcoder --resume ${sessionStatsRef.current.sessionId}`
|
|
1573
|
+
: undefined,
|
|
1574
|
+
});
|
|
1575
|
+
setDoneStatus(null);
|
|
1576
|
+
setExitPending(false);
|
|
1577
|
+
setOverlay(null);
|
|
1578
|
+
setLiveItems([]);
|
|
1579
|
+
setQuittingSummary(summary);
|
|
1580
|
+
writeStdout("\x1b[2J\x1b[3J\x1b[H");
|
|
1581
|
+
setTimeout(() => process.exit(0), 150);
|
|
1582
|
+
}, [currentModel, currentProvider, displayedCwd, writeStdout]);
|
|
1543
1583
|
// Consume pending post-remount work once on mount. Set by resetUI options
|
|
1544
1584
|
// for paths that remount AND immediately drive work (plan accept/reject,
|
|
1545
1585
|
// pixel fix, Goal approval). The work survives the unmount because
|
|
@@ -1610,7 +1650,7 @@ export function App(props) {
|
|
|
1610
1650
|
if (compactionAbortRef.current === ac)
|
|
1611
1651
|
compactionAbortRef.current = null;
|
|
1612
1652
|
},
|
|
1613
|
-
quit:
|
|
1653
|
+
quit: showSessionSummaryAndExit,
|
|
1614
1654
|
clearSession: () => {
|
|
1615
1655
|
if (props.resetUI) {
|
|
1616
1656
|
void (async () => {
|
|
@@ -1773,12 +1813,13 @@ export function App(props) {
|
|
|
1773
1813
|
props.resetUI,
|
|
1774
1814
|
props.sessionStore,
|
|
1775
1815
|
rebuildSystemPrompt,
|
|
1816
|
+
showSessionSummaryAndExit,
|
|
1776
1817
|
reloadCustomCommands,
|
|
1777
1818
|
replaceSystemPrompt,
|
|
1778
1819
|
setActiveGoalReferences,
|
|
1779
1820
|
setGoalModeAndPrompt,
|
|
1780
1821
|
]);
|
|
1781
|
-
const handleDoubleExit = useDoublePress(setExitPending,
|
|
1822
|
+
const handleDoubleExit = useDoublePress(setExitPending, showSessionSummaryAndExit);
|
|
1782
1823
|
const handleAbort = useCallback(() => {
|
|
1783
1824
|
if (agentLoop.isRunning) {
|
|
1784
1825
|
agentLoop.clearQueue();
|
|
@@ -3157,6 +3198,9 @@ export function App(props) {
|
|
|
3157
3198
|
: isPlanView
|
|
3158
3199
|
? "plan"
|
|
3159
3200
|
: null;
|
|
3201
|
+
if (quittingSummary) {
|
|
3202
|
+
return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: _jsx(SessionSummaryDisplay, { summary: quittingSummary }) }));
|
|
3203
|
+
}
|
|
3160
3204
|
return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: fullScreenOverlay ? (_jsx(FullScreenOverlayRouter, { overlay: fullScreenOverlay, version: props.version, cwd: props.cwd, agentRunning: agentLoop.isRunning, planAutoExpand: planAutoExpand, onClosePixel: handleCloseRemountableOverlay, onPixelFixOne: handlePixelFixOne, onPixelFixAll: handlePixelFixAll, onCloseSkills: handleCloseRemountableOverlay, onClosePlan: handleClosePlanOverlay, onApprovePlan: handleApprovePlan, onRejectPlan: handleRejectPlan })) : (_jsx(ChatScreen, { columns: columns, liveItems: uniqueItemsById(liveItems), renderItem: renderItem, isRunning: agentLoop.isRunning, visibleStreamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveStreamingSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, measuredLiveAreaRows: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, streamingContinuation: streamedAssistantFlushRef.current.flushedChars > 0, controlsRef: mainControlsRef, hiddenQueuedCount: hiddenQueuedCount, queueIndicatorMarginTop: shouldTopSpaceQueueIndicator ? 2 : 1, theme: theme, statusSlotVisible: statusSlotVisible, activityVisible: activityVisible, stallStatusVisible: stallStatusVisible, doneStatus: doneStatus, activityPhase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, isThinking: agentLoop.isThinking, thinkingLevel: thinkingLevel, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, lastUserMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, formatDuration: formatDuration, inputControls: {
|
|
3161
3205
|
onSubmit: handleSubmit,
|
|
3162
3206
|
onAbort: handleAbort,
|