* fix(pipeline): distinguish transport switch from real disconnect WS_FALLBACK errors (e.g. ready timeout → SSE fallback) are transport switches, not disconnections. No events are lost because lastEventId is preserved across the switch. Previously, every WS timeout triggered onDisconnect → onReconnect with a full resyncDirectoryAfterReconnect, which: - Missed idle parent sessions in candidate selection (root cause 1) - Could overwrite in-flight SSE state with stale fetch data (root cause 4) - Caused isConnected to flash false→true Now: WS_FALLBACK fires onTransportSwitch (sets isConnected only). Real disconnections (heartbeat timeout, network error) still fire the full onDisconnect → onReconnect → resync cycle. * fix(sync): relationship-aware reconnect with merge-not-replace Two changes to resyncDirectoryAfterReconnect: 1. Candidate selection now also includes parent sessions of any child sessions in the directory. Previously, if a child completed during the disconnect gap (busy→idle), neither child nor parent was selected because both appeared idle. The parent's task tool part would remain permanently stale. 2. Parent resync merges parts instead of replacing. Previously, the resync deleted parts for messages not in the fetch snapshot, which could erase parts delivered by SSE events that arrived between the fetch and the setState. Now only parts for messages in the snapshot are overwritten; everything else is preserved. * fix(sync): demand-load child session messages on access Bootstrap only loads session metadata — messages are populated exclusively by SSE events. When a user navigates to an old session that spawned subagents, child session messages were never in the store. Add useEnsureSessionMessages hook that detects this gap (session exists in state.session but state.message[sessionID] is absent) and triggers a background API fetch to load messages and parts. ToolPart already calls useSessionMessageRecords(taskSessionId) which returns empty when not loaded. Now it also calls useEnsureSessionMessages to populate the store on first access. * fix(sync): unmount-safe parent resync when child session goes idle When a child session transitions to idle (completes), the sync layer now schedules a targeted parts repair for the parent session's task tool part. Previously this only happened when the ToolPart component was mounted and had observed the child being active (taskChildSeenActive). This covers: - User navigated away while child was running - App restarted with active subagent sessions - SSE reconnect where child completed during disconnect Uses the existing repairSessionParts mechanism with its 5s cooldown to avoid redundant fetches. * fix: type-check fixes for sync-layer parent resync Fix TypeScript errors in Fix 5 implementation: - Convert currentSessionId from null to undefined for resolveFallbackTaskSessionId - Add default empty string for dir parameter in getScopedSdkClient - Use explicit sessionID parameter for scopedClient.session.messages All type-checks now pass. * fix(sync): address PR review feedback on deduplication - Use enqueuePartsRepair for session.idle parent resync instead of direct repairSessionParts call. enqueuePartsRepair already has a 5s cooldown to prevent redundant parallel API calls when multiple child sessions go idle concurrently. - Move useEnsureSessionMessages loading guard from component-scoped React.useRef to a module-level Set keyed by directory:sessionID. Prevents parallel fetches when multiple ToolPart instances mount for the same child session. * fix(sync): add missing semicolon on useEnsureSessionMessages call Address Greptile P2 review comment on ToolPart.tsx:1931.
159 lines
3.4 KiB
TypeScript
159 lines
3.4 KiB
TypeScript
// Core utilities
|
|
export { Binary } from "./binary"
|
|
export { retry, type RetryOptions } from "./retry"
|
|
|
|
// Types
|
|
export type { State, GlobalState, ProjectMeta, DirState, EvictPlan, DisposeCheck, ChildOptions } from "./types"
|
|
export {
|
|
INITIAL_STATE,
|
|
INITIAL_GLOBAL_STATE,
|
|
MAX_DIR_STORES,
|
|
DIR_IDLE_TTL_MS,
|
|
SESSION_CACHE_LIMIT,
|
|
SESSION_RECENT_LIMIT,
|
|
SESSION_RECENT_WINDOW,
|
|
} from "./types"
|
|
|
|
// Eviction
|
|
export { pickDirectoriesToEvict, canDisposeDirectory } from "./eviction"
|
|
|
|
// Session cache
|
|
export { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache"
|
|
|
|
// Optimistic
|
|
export {
|
|
applyOptimisticAdd,
|
|
applyOptimisticRemove,
|
|
mergeOptimisticPage,
|
|
mergeMessages,
|
|
type OptimisticItem,
|
|
type OptimisticStore,
|
|
type OptimisticAddInput,
|
|
type OptimisticRemoveInput,
|
|
type MessagePage,
|
|
} from "./optimistic"
|
|
|
|
// Event reducer
|
|
export {
|
|
reduceGlobalEvent,
|
|
applyGlobalProject,
|
|
applyDirectoryEvent,
|
|
type GlobalEventResult,
|
|
} from "./event-reducer"
|
|
|
|
// Event pipeline
|
|
export { createEventPipeline, type QueuedEvent, type FlushHandler } from "./event-pipeline"
|
|
|
|
// Stores
|
|
export { useGlobalSyncStore, type GlobalSyncStore } from "./global-sync-store"
|
|
export { ChildStoreManager, type DirectoryStore } from "./child-store"
|
|
|
|
// Bootstrap
|
|
export { bootstrapGlobal, bootstrapDirectory } from "./bootstrap"
|
|
|
|
// React integration
|
|
export {
|
|
SyncProvider,
|
|
useGlobalSync,
|
|
useGlobalSyncSelector,
|
|
useDirectoryStore,
|
|
useDirectorySync,
|
|
useSessionMessages,
|
|
useSessionMessageCount,
|
|
useSessionMessagesResolved,
|
|
useSessionParts,
|
|
useSessionStatus,
|
|
useSessionPermissions,
|
|
useSessionQuestions,
|
|
useSessions,
|
|
useSyncSDK,
|
|
useSyncDirectory,
|
|
useChildStoreManager,
|
|
useSessionMessageRecords,
|
|
useEnsureSessionMessages,
|
|
useSessionTextMessages,
|
|
useUserMessageHistory,
|
|
buildSessionMessageRecordsSnapshot,
|
|
} from "./sync-context"
|
|
|
|
// Sync operations
|
|
export { useSync } from "./use-sync"
|
|
|
|
// Prompt submission
|
|
export { usePromptSubmit, type SubmitInput } from "./submit"
|
|
|
|
|
|
// Streaming lifecycle
|
|
export {
|
|
useStreamingStore,
|
|
updateStreamingState,
|
|
selectStreamingMessageId,
|
|
selectMessageStreamState,
|
|
selectIsStreaming,
|
|
type StreamPhase,
|
|
type MessageStreamState,
|
|
type StreamingStore,
|
|
} from "./streaming"
|
|
|
|
// Session UI state
|
|
export {
|
|
useSessionUIStore,
|
|
type SessionUIState,
|
|
type AttachedFile,
|
|
type NewSessionDraftState,
|
|
} from "./session-ui-store"
|
|
|
|
// Input store (pending input, synthetic parts, attached files)
|
|
export { useInputStore, type SyntheticContextPart } from "./input-store"
|
|
|
|
// Viewport store (per-session scroll anchors, memory state)
|
|
export {
|
|
useViewportStore,
|
|
type SessionMemoryState,
|
|
type ViewportState,
|
|
} from "./viewport-store"
|
|
|
|
// Sync refs (imperative access from non-React code)
|
|
export {
|
|
setSyncRefs,
|
|
getSyncSDK,
|
|
getSyncChildStores,
|
|
getSyncDirectory,
|
|
getDirectoryState,
|
|
getSyncSessions,
|
|
getSyncMessages,
|
|
getSyncParts,
|
|
getSyncSessionStatus,
|
|
getSyncPermissions,
|
|
getSyncQuestions,
|
|
} from "./sync-refs"
|
|
|
|
// Persisted metadata caches
|
|
export {
|
|
readDirCache,
|
|
persistVcs,
|
|
persistProjectMeta,
|
|
persistIcon,
|
|
clearDirCache,
|
|
type PersistedDirCache,
|
|
} from "./persist-cache"
|
|
|
|
// Session actions
|
|
export {
|
|
setActionRefs,
|
|
createSession,
|
|
deleteSession,
|
|
archiveSession,
|
|
updateSessionTitle,
|
|
shareSession,
|
|
unshareSession,
|
|
optimisticSend,
|
|
abortCurrentOperation,
|
|
respondToPermission,
|
|
dismissPermission,
|
|
respondToQuestion,
|
|
rejectQuestion,
|
|
revertToMessage,
|
|
forkFromMessage,
|
|
} from "./session-actions"
|