@kortix/sdk 0.10.13 → 0.10.14
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 +34 -11
- package/dist/browser/session-sync/session-sync-registry.d.ts +39 -0
- package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -0
- package/dist/browser/session-sync/session-sync-registry.js +146 -0
- package/dist/browser/session-sync/session-sync-registry.js.map +1 -0
- package/dist/core/client/kortix.d.ts +20 -4
- package/dist/core/client/kortix.d.ts.map +1 -1
- package/dist/core/client/kortix.js +35 -3
- package/dist/core/client/kortix.js.map +1 -1
- package/dist/core/http/api-client.d.ts +9 -0
- package/dist/core/http/api-client.d.ts.map +1 -1
- package/dist/core/http/api-client.js +27 -1
- package/dist/core/http/api-client.js.map +1 -1
- package/dist/core/http/config.d.ts +1 -0
- package/dist/core/http/config.d.ts.map +1 -1
- package/dist/core/http/config.js.map +1 -1
- package/dist/core/http/feature-flags.d.ts +1 -0
- package/dist/core/http/feature-flags.d.ts.map +1 -1
- package/dist/core/http/feature-flags.js +2 -13
- package/dist/core/http/feature-flags.js.map +1 -1
- package/dist/core/rest/projects-client/connectors.d.ts +37 -0
- package/dist/core/rest/projects-client/connectors.d.ts.map +1 -1
- package/dist/core/rest/projects-client/connectors.js +9 -0
- package/dist/core/rest/projects-client/connectors.js.map +1 -1
- package/dist/core/rest/projects-client/github.d.ts +30 -1
- package/dist/core/rest/projects-client/github.d.ts.map +1 -1
- package/dist/core/rest/projects-client/github.js +12 -1
- package/dist/core/rest/projects-client/github.js.map +1 -1
- package/dist/core/rest/projects-client/model-defaults.d.ts +1 -1
- package/dist/core/rest/projects-client/model-defaults.d.ts.map +1 -1
- package/dist/core/rest/projects-client/model-defaults.js.map +1 -1
- package/dist/core/rest/projects-client/projects.d.ts +82 -2
- package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
- package/dist/core/rest/projects-client/projects.js +18 -6
- package/dist/core/rest/projects-client/projects.js.map +1 -1
- package/dist/core/rest/projects-client/session-sandbox.d.ts +10 -3
- package/dist/core/rest/projects-client/session-sandbox.d.ts.map +1 -1
- package/dist/core/rest/projects-client/session-sandbox.js +48 -10
- package/dist/core/rest/projects-client/session-sandbox.js.map +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +46 -2
- package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
- package/dist/core/rest/projects-client/sessions.js +3 -2
- package/dist/core/rest/projects-client/sessions.js.map +1 -1
- package/dist/core/rest/projects-client/triggers.d.ts +43 -11
- package/dist/core/rest/projects-client/triggers.d.ts.map +1 -1
- package/dist/core/rest/projects-client/triggers.js.map +1 -1
- package/dist/core/session-sync/session-sync-controller.d.ts +89 -0
- package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -0
- package/dist/core/session-sync/session-sync-controller.js +248 -0
- package/dist/core/session-sync/session-sync-controller.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kortix.esm.min.js +31 -31
- package/dist/kortix.global.js +31 -31
- package/dist/node/server.d.ts +0 -21
- package/dist/node/server.d.ts.map +1 -1
- package/dist/node/server.js +23 -1
- package/dist/node/server.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/model-flatten.d.ts.map +1 -1
- package/dist/react/model-flatten.js +4 -0
- package/dist/react/model-flatten.js.map +1 -1
- package/dist/react/provider-selection.d.ts.map +1 -1
- package/dist/react/provider-selection.js +3 -2
- package/dist/react/provider-selection.js.map +1 -1
- package/dist/react/pty-query-state.d.ts +3 -0
- package/dist/react/pty-query-state.d.ts.map +1 -0
- package/dist/react/pty-query-state.js +7 -0
- package/dist/react/pty-query-state.js.map +1 -0
- package/dist/react/session-start-stash.d.ts.map +1 -1
- package/dist/react/session-start-stash.js +17 -4
- package/dist/react/session-start-stash.js.map +1 -1
- package/dist/react/use-model-store.d.ts.map +1 -1
- package/dist/react/use-model-store.js +3 -5
- package/dist/react/use-model-store.js.map +1 -1
- package/dist/react/use-opencode-events/handle-event.d.ts +2 -0
- package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
- package/dist/react/use-opencode-events/handle-event.js +13 -28
- package/dist/react/use-opencode-events/handle-event.js.map +1 -1
- package/dist/react/use-opencode-events/index.d.ts.map +1 -1
- package/dist/react/use-opencode-events/index.js +7 -13
- package/dist/react/use-opencode-events/index.js.map +1 -1
- package/dist/react/use-opencode-local.d.ts +6 -7
- package/dist/react/use-opencode-local.d.ts.map +1 -1
- package/dist/react/use-opencode-local.js +18 -32
- package/dist/react/use-opencode-local.js.map +1 -1
- package/dist/react/use-opencode-pty.d.ts +2 -0
- package/dist/react/use-opencode-pty.d.ts.map +1 -1
- package/dist/react/use-opencode-pty.js +15 -7
- package/dist/react/use-opencode-pty.js.map +1 -1
- package/dist/react/use-opencode-sessions/no-compaction-model-error.d.ts +31 -0
- package/dist/react/use-opencode-sessions/no-compaction-model-error.d.ts.map +1 -0
- package/dist/react/use-opencode-sessions/no-compaction-model-error.js +34 -0
- package/dist/react/use-opencode-sessions/no-compaction-model-error.js.map +1 -0
- package/dist/react/use-opencode-sessions/sessions.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/sessions.js +12 -2
- package/dist/react/use-opencode-sessions/sessions.js.map +1 -1
- package/dist/react/use-session-prefetch.d.ts +4 -14
- package/dist/react/use-session-prefetch.d.ts.map +1 -1
- package/dist/react/use-session-prefetch.js +13 -73
- package/dist/react/use-session-prefetch.js.map +1 -1
- package/dist/react/use-session-send.d.ts +1 -0
- package/dist/react/use-session-send.d.ts.map +1 -1
- package/dist/react/use-session-send.js +2 -1
- package/dist/react/use-session-send.js.map +1 -1
- package/dist/react/use-session-sync.d.ts +10 -8
- package/dist/react/use-session-sync.d.ts.map +1 -1
- package/dist/react/use-session-sync.js +39 -327
- package/dist/react/use-session-sync.js.map +1 -1
- package/dist/transcript.d.ts +30 -1
- package/dist/transcript.d.ts.map +1 -1
- package/dist/transcript.js +13 -16
- package/dist/transcript.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,83 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
import { useCallback
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const inFlightPrefetches = new Map();
|
|
10
|
-
/**
|
|
11
|
-
* Prefetch a single session's messages into the sync store + IDB cache.
|
|
12
|
-
* Skips if the session is already in the sync store.
|
|
13
|
-
*/
|
|
14
|
-
export async function prefetchSession(sessionId) {
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { ACTIVE_SESSION_PREFETCH_SOURCE, clearActiveSessionPrefetches, prefetchSessionSyncOnce, } from '../browser/session-sync/session-sync-registry.js';
|
|
4
|
+
import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-store.js';
|
|
5
|
+
import { getClientForUrl } from '../core/runtime/client.js';
|
|
6
|
+
import { canQueryOpenCodeSession } from './use-opencode-sessions/index.js';
|
|
7
|
+
/** Load a bounded session tail before navigation or through a known runtime URL. */
|
|
8
|
+
export async function prefetchSession(sessionId, runtimeUrl) {
|
|
15
9
|
if (!canQueryOpenCodeSession(sessionId))
|
|
16
10
|
return;
|
|
17
|
-
if (useSandboxConnectionStore.getState().healthy !== true)
|
|
11
|
+
if (!runtimeUrl && useSandboxConnectionStore.getState().healthy !== true)
|
|
18
12
|
return;
|
|
19
|
-
|
|
20
|
-
return;
|
|
21
|
-
const existingPrefetch = inFlightPrefetches.get(sessionId);
|
|
22
|
-
if (existingPrefetch)
|
|
23
|
-
return existingPrefetch;
|
|
24
|
-
const run = (async () => {
|
|
25
|
-
const state = useSyncStore.getState();
|
|
26
|
-
if (sessionId in state.messages && (state.messages[sessionId]?.length ?? 0) > 0) {
|
|
27
|
-
prefetchedSessions.add(sessionId);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
// Try IDB cache first
|
|
31
|
-
const cached = await loadSessionFromIDB(sessionId);
|
|
32
|
-
if (cached && cached.messages.length > 0) {
|
|
33
|
-
const currentState = useSyncStore.getState();
|
|
34
|
-
if (!(sessionId in currentState.messages) || (currentState.messages[sessionId]?.length ?? 0) === 0) {
|
|
35
|
-
currentState.hydrate(sessionId, cached.messages.map((info) => ({
|
|
36
|
-
info,
|
|
37
|
-
parts: cached.parts[info.id] ?? [],
|
|
38
|
-
})));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Fetch fresh data from server in background
|
|
42
|
-
try {
|
|
43
|
-
const res = await getClient().session.messages({ sessionID: sessionId });
|
|
44
|
-
const data = res.data ?? [];
|
|
45
|
-
if (data.length > 0) {
|
|
46
|
-
useSyncStore.getState().hydrate(sessionId, data);
|
|
47
|
-
const parts = useSyncStore.getState().parts;
|
|
48
|
-
const msgs = useSyncStore.getState().messages[sessionId] ?? [];
|
|
49
|
-
saveSessionToIDB(sessionId, msgs, parts);
|
|
50
|
-
}
|
|
51
|
-
prefetchedSessions.add(sessionId);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Non-critical — cache is still warm from IDB
|
|
55
|
-
}
|
|
56
|
-
})().finally(() => {
|
|
57
|
-
inFlightPrefetches.delete(sessionId);
|
|
58
|
-
});
|
|
59
|
-
inFlightPrefetches.set(sessionId, run);
|
|
60
|
-
return run;
|
|
13
|
+
await prefetchSessionSyncOnce(sessionId, runtimeUrl ?? ACTIVE_SESSION_PREFETCH_SOURCE, runtimeUrl ? getClientForUrl(runtimeUrl) : undefined);
|
|
61
14
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Reset prefetch tracking (e.g., on server switch).
|
|
64
|
-
*/
|
|
65
15
|
export function resetPrefetchState() {
|
|
66
|
-
|
|
67
|
-
inFlightPrefetches.clear();
|
|
16
|
+
clearActiveSessionPrefetches();
|
|
68
17
|
}
|
|
69
|
-
|
|
70
|
-
* Hook: background-prefetch top N sessions after session list loads.
|
|
71
|
-
* Uses requestIdleCallback to avoid blocking UI.
|
|
72
|
-
*/
|
|
73
|
-
export function useBackgroundSessionPrefetch(sessions) {
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (!sessions || sessions.length === 0)
|
|
76
|
-
return;
|
|
77
|
-
void pruneIDBCache();
|
|
78
|
-
}, [sessions]);
|
|
18
|
+
export function useBackgroundSessionPrefetch(_sessions) {
|
|
79
19
|
const prefetchOnHover = useCallback((sessionId) => {
|
|
80
|
-
prefetchSession(sessionId);
|
|
20
|
+
void prefetchSession(sessionId);
|
|
81
21
|
}, []);
|
|
82
22
|
return { prefetchOnHover };
|
|
83
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-prefetch.js","sourceRoot":"","sources":["../../src/react/use-session-prefetch.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"use-session-prefetch.js","sourceRoot":"","sources":["../../src/react/use-session-prefetch.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAgB,MAAM,yBAAyB,CAAC;AAEhF,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,UAAmB;IAC1E,IAAI,CAAC,uBAAuB,CAAC,SAAS,CAAC;QAAE,OAAO;IAChD,IAAI,CAAC,UAAU,IAAI,yBAAyB,CAAC,QAAQ,EAAE,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO;IACjF,MAAM,uBAAuB,CAC3B,SAAS,EACT,UAAU,IAAI,8BAA8B,EAC5C,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,4BAA4B,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,SAAgC;IAC3E,MAAM,eAAe,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,EAAE;QACxD,KAAK,eAAe,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,eAAe,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-send.d.ts","sourceRoot":"","sources":["../../src/react/use-session-send.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-session-send.d.ts","sourceRoot":"","sources":["../../src/react/use-session-send.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback, useState } from 'react';
|
|
3
3
|
import { getClient } from '../core/runtime/client.js';
|
|
4
|
+
import { SESSION_SYNC_PAGE_SIZE } from '../core/session-sync/session-sync-controller.js';
|
|
4
5
|
import { ascendingId, useSyncStore } from '../browser/stores/sync-store.js';
|
|
5
6
|
import { classifySendError } from './use-session.js';
|
|
6
7
|
import { promptOpenCodeMessage, useAbortOpenCodeSession, } from './use-opencode-sessions/index.js';
|
|
@@ -73,7 +74,7 @@ export function recoverFromSendFailure(sessionId, messageId, error, options = {}
|
|
|
73
74
|
return classified;
|
|
74
75
|
}
|
|
75
76
|
client.session
|
|
76
|
-
.messages({ sessionID: sessionId })
|
|
77
|
+
.messages({ sessionID: sessionId, limit: SESSION_SYNC_PAGE_SIZE })
|
|
77
78
|
.then((res) => {
|
|
78
79
|
if (res?.data) {
|
|
79
80
|
// hydrate() already drops superseded optimistic messages AND bridges
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-send.js","sourceRoot":"","sources":["../../src/react/use-session-send.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAqCb,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEzE,OAAO,EAAE,iBAAiB,EAAwB,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAGxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAmB,MAAM,uBAAuB,CAAC;AAEzF,+EAA+E;AAC/E,2EAA2E;AAC3E,aAAa;AACb,+EAA+E;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,SAAiB,EACjB,IAAY,EACZ,OAAkB;IAElB,MAAM,KAAK,GAAW,IAAI,CAAC,IAAI,EAAE;QAC/B,CAAC,CAAC;YACE;gBACE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC;gBACtC,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,SAAS;gBACpB,IAAI,EAAE,MAAe;gBACrB,IAAI;aACL;SACF;QACH,CAAC,CAAC,EAAE,CAAC;IACP,wEAAwE;IACxE,oEAAoE;IACpE,wDAAwD;IACxD,MAAM,IAAI,GAAG;QACX,EAAE,EAAE,SAAS;QACb,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;KACnB,CAAC;IACb,YAAY,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9D,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB,EAAE,SAAiB;IACxE,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/D,YAAY,CAAC,QAAQ,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACjE,CAAC;AAgCD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,SAAiB,EACjB,KAAc,EACd,UAA+B,EAAE;IAEjC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IACvD,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,IAAK,SAAqD,CAAC;IAClG,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEnC,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAE/D,IAAI,MAA8B,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,aAAa,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,YAAY,CAAC,QAAQ,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC/D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,OAAO;SACX,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"use-session-send.js","sourceRoot":"","sources":["../../src/react/use-session-send.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import type { SessionStatus, Todo } from
|
|
1
|
+
import type { SessionStatus, Todo } from '@opencode-ai/sdk/v2/client';
|
|
2
|
+
import { loadSessionTranscriptMessages } from '../browser/session-sync/session-sync-registry.js';
|
|
3
|
+
import { type MessageWithParts } from '../browser/stores/sync-store.js';
|
|
4
|
+
export { loadSessionTranscriptMessages };
|
|
2
5
|
type FileDiff = Omit<import('@opencode-ai/sdk/v2/client').SnapshotFileDiff, 'patch'> & {
|
|
3
6
|
patch?: string;
|
|
4
7
|
before?: string;
|
|
5
8
|
after?: string;
|
|
6
9
|
};
|
|
7
|
-
import { type MessageWithParts } from "../browser/stores/sync-store.js";
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* On first access, fetches messages from the server and populates the store.
|
|
13
|
-
* After that, SSE events keep the store updated in real time.
|
|
11
|
+
* Returns the current session tail and explicit history-loading state.
|
|
12
|
+
* Network synchronization lives in the framework-free SessionSyncController.
|
|
14
13
|
*/
|
|
15
14
|
export declare function useSessionSync(sessionId: string): {
|
|
16
15
|
messages: MessageWithParts[];
|
|
17
16
|
status: SessionStatus;
|
|
17
|
+
freshness: import("../core/session-sync/session-sync-controller.js").SessionSyncFreshness;
|
|
18
18
|
isBusy: boolean;
|
|
19
19
|
isLoading: boolean;
|
|
20
|
+
hasOlder: boolean;
|
|
21
|
+
isLoadingOlder: boolean;
|
|
22
|
+
loadOlder: () => Promise<void>;
|
|
20
23
|
diffs: FileDiff[];
|
|
21
24
|
todos: Todo[];
|
|
22
25
|
};
|
|
23
|
-
export {};
|
|
24
26
|
//# sourceMappingURL=use-session-sync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-sync.d.ts","sourceRoot":"","sources":["../../src/react/use-session-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-session-sync.d.ts","sourceRoot":"","sources":["../../src/react/use-session-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,aAAa,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAErF,OAAO,EAEL,6BAA6B,EAE9B,MAAM,+CAA+C,CAAC;AAEvD,OAAO,EAAE,KAAK,gBAAgB,EAAgB,MAAM,8BAA8B,CAAC;AAGnF,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAEzC,KAAK,QAAQ,GAAG,IAAI,CAAC,OAAO,4BAA4B,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAqDF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;EAkD/C"}
|
|
@@ -1,36 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import { useEffect,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useSyncExternalStore } from 'react';
|
|
3
|
+
import { getSessionSyncController, loadSessionTranscriptMessages, retainSessionSyncController, } from '../browser/session-sync/session-sync-registry.js';
|
|
4
|
+
import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-store.js';
|
|
5
|
+
import { useSyncStore } from '../browser/stores/sync-store.js';
|
|
6
|
+
import { canQueryOpenCodeSession } from './use-opencode-sessions/index.js';
|
|
7
|
+
export { loadSessionTranscriptMessages };
|
|
8
8
|
const EMPTY_MESSAGES = [];
|
|
9
9
|
const EMPTY_DIFFS = [];
|
|
10
10
|
const EMPTY_TODOS = [];
|
|
11
|
-
const IDLE_STATUS = { type:
|
|
12
|
-
const MESSAGE_POLL_INTERVAL_MS = 10_000;
|
|
13
|
-
const NETWORK_REVALIDATE_COOLDOWN_MS = 60_000;
|
|
14
|
-
/**
|
|
15
|
-
* Build MessageWithParts[] with reference caching.
|
|
16
|
-
* Returns the same array reference if nothing relevant changed.
|
|
17
|
-
* This is a module-level cache keyed by sessionId so multiple components
|
|
18
|
-
* using the same sessionId share the cache (e.g. SessionLayout + SessionChat).
|
|
19
|
-
*/
|
|
11
|
+
const IDLE_STATUS = { type: 'idle' };
|
|
20
12
|
const MESSAGE_CACHE_MAX = 20;
|
|
21
13
|
const messageCache = new Map();
|
|
22
|
-
const inFlightInitialLoads = new Map();
|
|
23
|
-
const lastNetworkLoadAttemptAt = new Map();
|
|
24
|
-
const pollInFlightSessions = new Set();
|
|
25
|
-
function markNetworkLoadAttempt(sessionId) {
|
|
26
|
-
lastNetworkLoadAttemptAt.delete(sessionId);
|
|
27
|
-
lastNetworkLoadAttemptAt.set(sessionId, Date.now());
|
|
28
|
-
if (lastNetworkLoadAttemptAt.size > MESSAGE_CACHE_MAX) {
|
|
29
|
-
const oldest = lastNetworkLoadAttemptAt.keys().next().value;
|
|
30
|
-
if (oldest)
|
|
31
|
-
lastNetworkLoadAttemptAt.delete(oldest);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
14
|
function touchMessageCache(sessionId) {
|
|
35
15
|
const entry = messageCache.get(sessionId);
|
|
36
16
|
if (entry) {
|
|
@@ -48,326 +28,58 @@ function buildMessages(sessionId, msgs, parts) {
|
|
|
48
28
|
return EMPTY_MESSAGES;
|
|
49
29
|
const cached = messageCache.get(sessionId);
|
|
50
30
|
if (cached && cached.msgs === msgs) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (same) {
|
|
54
|
-
for (let i = 0; i < msgs.length; i++) {
|
|
55
|
-
if (parts[msgs[i].id] !== cached.partRefs[i]) {
|
|
56
|
-
same = false;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
31
|
+
const same = cached.partRefs.length === msgs.length &&
|
|
32
|
+
msgs.every((message, index) => Object.is(parts[message.id], cached.partRefs[index]));
|
|
61
33
|
if (same)
|
|
62
34
|
return cached.result;
|
|
63
35
|
}
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
partRefs.push(pa);
|
|
70
|
-
result.push({ info, parts: pa ?? [] });
|
|
71
|
-
}
|
|
36
|
+
const partRefs = msgs.map((message) => parts[message.id]);
|
|
37
|
+
const result = msgs.map((info) => ({
|
|
38
|
+
info,
|
|
39
|
+
parts: parts[info.id] ?? [],
|
|
40
|
+
}));
|
|
72
41
|
messageCache.set(sessionId, { msgs, partRefs, result });
|
|
73
42
|
touchMessageCache(sessionId);
|
|
74
43
|
return result;
|
|
75
44
|
}
|
|
76
45
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* On first access, fetches messages from the server and populates the store.
|
|
81
|
-
* After that, SSE events keep the store updated in real time.
|
|
46
|
+
* Returns the current session tail and explicit history-loading state.
|
|
47
|
+
* Network synchronization lives in the framework-free SessionSyncController.
|
|
82
48
|
*/
|
|
83
49
|
export function useSessionSync(sessionId) {
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
// On failure, retries with backoff (500ms, 1s, 2s) up to 3 times.
|
|
88
|
-
// Without retry, a transient failure (server not ready on page refresh)
|
|
89
|
-
// permanently prevents messages from loading because fetchedRef blocks re-fetch.
|
|
50
|
+
const runtimeHealthy = useSandboxConnectionStore((state) => state.healthy === true);
|
|
51
|
+
const controller = getSessionSyncController(sessionId);
|
|
52
|
+
const sync = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
|
|
90
53
|
useEffect(() => {
|
|
91
|
-
if (!canQueryOpenCodeSession(sessionId))
|
|
54
|
+
if (!canQueryOpenCodeSession(sessionId) || !runtimeHealthy)
|
|
92
55
|
return;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// NOTE: We intentionally do NOT skip the fetch when the store already
|
|
98
|
-
// has messages from SSE. SSE only delivers events from the connection
|
|
99
|
-
// point forward — it doesn't replay history. If the agent is actively
|
|
100
|
-
// streaming when the user navigates to a session, SSE stub messages
|
|
101
|
-
// would be the only messages in the store, missing the full thread
|
|
102
|
-
// history. Always fetch and let hydrate() merge safely.
|
|
103
|
-
let cancelled = false;
|
|
104
|
-
// Phase 2: Hydrate from IndexedDB cache FIRST for instant display.
|
|
105
|
-
// Server fetch still runs in background to revalidate.
|
|
106
|
-
const hydrateFromCache = async () => {
|
|
107
|
-
const existing = useSyncStore.getState().messages[sessionId];
|
|
108
|
-
if (existing && existing.length > 0)
|
|
109
|
-
return;
|
|
110
|
-
try {
|
|
111
|
-
const cached = await loadSessionFromIDB(sessionId);
|
|
112
|
-
if (cancelled)
|
|
113
|
-
return;
|
|
114
|
-
if (cached && cached.messages.length > 0) {
|
|
115
|
-
const current = useSyncStore.getState().messages[sessionId];
|
|
116
|
-
if (!current || current.length === 0) {
|
|
117
|
-
useSyncStore.getState().hydrate(sessionId, cached.messages.map((info) => ({
|
|
118
|
-
info,
|
|
119
|
-
parts: cached.parts[info.id] ?? [],
|
|
120
|
-
})));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
// IDB unavailable — fall through to network fetch
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
hydrateFromCache();
|
|
129
|
-
if (!runtimeHealthy) {
|
|
130
|
-
fetchedRef.current = null;
|
|
131
|
-
return () => {
|
|
132
|
-
cancelled = true;
|
|
133
|
-
messageCache.delete(sessionId);
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
const existingMessages = useSyncStore.getState().messages[sessionId];
|
|
137
|
-
const lastNetworkLoad = lastNetworkLoadAttemptAt.get(sessionId) ?? 0;
|
|
138
|
-
if (existingMessages &&
|
|
139
|
-
existingMessages.length > 0 &&
|
|
140
|
-
Date.now() - lastNetworkLoad < NETWORK_REVALIDATE_COOLDOWN_MS) {
|
|
141
|
-
return () => {
|
|
142
|
-
cancelled = true;
|
|
143
|
-
fetchedRef.current = null;
|
|
144
|
-
messageCache.delete(sessionId);
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
// IMPORTANT: do NOT short-circuit hydrate on `cancelled`. The
|
|
148
|
-
// store is keyed by sessionID — writing into it is always safe
|
|
149
|
-
// even if this effect's component has unmounted. Strict Mode
|
|
150
|
-
// fires effect → cleanup (cancelled=true) → effect, and the
|
|
151
|
-
// in-flight dedup map below would otherwise drop the only fetch
|
|
152
|
-
// because the cancelled first run bails before hydrating.
|
|
153
|
-
const fetchWithRetry = async (attempt = 0) => {
|
|
154
|
-
try {
|
|
155
|
-
const res = await getClient().session.messages({
|
|
156
|
-
sessionID: sessionId,
|
|
157
|
-
});
|
|
158
|
-
const data = res.data ?? [];
|
|
159
|
-
if (data.length === 0) {
|
|
160
|
-
const freshState = useSyncStore.getState();
|
|
161
|
-
const existingMsgs = freshState.messages[sessionId];
|
|
162
|
-
if (existingMsgs && existingMsgs.length > 0) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
// Mark session as loaded (empty) immediately so
|
|
166
|
-
// isLoading becomes false — no extra round-trip.
|
|
167
|
-
// If the session is busy (agent running), SSE events
|
|
168
|
-
// will deliver messages as they arrive.
|
|
169
|
-
freshState.clearSession(sessionId);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (res.data) {
|
|
173
|
-
useSyncStore.getState().hydrate(sessionId, res.data);
|
|
174
|
-
// Persist to IDB for next cold load
|
|
175
|
-
const state = useSyncStore.getState();
|
|
176
|
-
const msgs = state.messages[sessionId] ?? [];
|
|
177
|
-
if (msgs.length > 0) {
|
|
178
|
-
saveSessionToIDB(sessionId, msgs, state.parts);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
// Stop retrying ONLY if the effect has been torn down
|
|
184
|
-
// (no live consumer wants this data anymore). Don't gate
|
|
185
|
-
// the success-path hydrate on `cancelled` — see comment
|
|
186
|
-
// above the function.
|
|
187
|
-
if (cancelled)
|
|
188
|
-
return;
|
|
189
|
-
if (attempt < 3) {
|
|
190
|
-
const delay = 500 * 2 ** attempt;
|
|
191
|
-
setTimeout(() => fetchWithRetry(attempt + 1), delay);
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
// All retries exhausted — unblock the UI by marking
|
|
195
|
-
// the session as loaded (empty). Without this,
|
|
196
|
-
// isLoading stays true forever on cold boot when the
|
|
197
|
-
// sandbox isn't ready yet.
|
|
198
|
-
fetchedRef.current = null;
|
|
199
|
-
const state = useSyncStore.getState();
|
|
200
|
-
if (!(sessionId in state.messages)) {
|
|
201
|
-
state.clearSession(sessionId);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const startFetch = () => {
|
|
207
|
-
const loadPromise = fetchWithRetry().finally(() => {
|
|
208
|
-
markNetworkLoadAttempt(sessionId);
|
|
209
|
-
inFlightInitialLoads.delete(sessionId);
|
|
210
|
-
});
|
|
211
|
-
inFlightInitialLoads.set(sessionId, loadPromise);
|
|
212
|
-
return loadPromise;
|
|
213
|
-
};
|
|
214
|
-
const existingLoad = inFlightInitialLoads.get(sessionId);
|
|
215
|
-
if (existingLoad) {
|
|
216
|
-
// A prior mount fired the fetch. In Strict Mode that prior run
|
|
217
|
-
// may have been cancelled mid-flight and skipped hydrate.
|
|
218
|
-
// Await it, then verify the store actually has data — re-fire
|
|
219
|
-
// if it doesn't.
|
|
220
|
-
existingLoad
|
|
221
|
-
.catch(() => {
|
|
222
|
-
// Error handling is already performed by the owner.
|
|
223
|
-
})
|
|
224
|
-
.then(() => {
|
|
225
|
-
if (cancelled)
|
|
226
|
-
return;
|
|
227
|
-
const after = useSyncStore.getState().messages[sessionId];
|
|
228
|
-
if (after === undefined && !inFlightInitialLoads.has(sessionId)) {
|
|
229
|
-
startFetch();
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
startFetch();
|
|
235
|
-
}
|
|
56
|
+
const release = retainSessionSyncController(sessionId);
|
|
57
|
+
// Cached messages render immediately. Revalidate one bounded tail so
|
|
58
|
+
// events produced while this route was inactive are not skipped.
|
|
59
|
+
void controller.reconcile('initial');
|
|
236
60
|
return () => {
|
|
237
|
-
|
|
238
|
-
// Allow a future real remount/navigation to fetch again while still
|
|
239
|
-
// relying on the module-level in-flight map to dedupe Strict Mode.
|
|
240
|
-
fetchedRef.current = null;
|
|
241
|
-
// Evict stale cache entry to prevent unbounded memory growth
|
|
61
|
+
release();
|
|
242
62
|
messageCache.delete(sessionId);
|
|
243
63
|
};
|
|
244
|
-
}, [
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const pollTimerRef = useRef(null);
|
|
252
|
-
const lastPartCountRef = useRef(0);
|
|
253
|
-
const pollInFlightRef = useRef(false);
|
|
64
|
+
}, [controller, runtimeHealthy, sessionId]);
|
|
65
|
+
const messages = useSyncStore((state) => buildMessages(sessionId, state.messages[sessionId], state.parts));
|
|
66
|
+
const status = useSyncStore((state) => state.sessionStatus[sessionId] ?? IDLE_STATUS);
|
|
67
|
+
const diffs = useSyncStore((state) => state.diffs[sessionId]);
|
|
68
|
+
const todos = useSyncStore((state) => state.todos[sessionId]);
|
|
69
|
+
const isBusy = status.type === 'busy' || status.type === 'retry';
|
|
70
|
+
const isLoading = !useSyncStore((state) => sessionId in state.messages);
|
|
254
71
|
useEffect(() => {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (!runtimeHealthy) {
|
|
258
|
-
if (pollTimerRef.current) {
|
|
259
|
-
clearInterval(pollTimerRef.current);
|
|
260
|
-
pollTimerRef.current = null;
|
|
261
|
-
}
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const state = useSyncStore.getState();
|
|
265
|
-
const currentStatus = state.sessionStatus[sessionId];
|
|
266
|
-
const isBusyNow = currentStatus?.type === "busy" || currentStatus?.type === "retry";
|
|
267
|
-
if (isBusyNow) {
|
|
268
|
-
// Start polling if not already active
|
|
269
|
-
if (!pollTimerRef.current) {
|
|
270
|
-
// Track part count to detect SSE liveness — if parts are
|
|
271
|
-
// growing between polls, SSE is working and we skip the fetch.
|
|
272
|
-
const countParts = () => {
|
|
273
|
-
const s = useSyncStore.getState();
|
|
274
|
-
const msgs = s.messages[sessionId] ?? [];
|
|
275
|
-
let count = 0;
|
|
276
|
-
for (const m of msgs) {
|
|
277
|
-
const parts = s.parts[m.id];
|
|
278
|
-
if (parts)
|
|
279
|
-
count += parts.length;
|
|
280
|
-
}
|
|
281
|
-
return count;
|
|
282
|
-
};
|
|
283
|
-
lastPartCountRef.current = countParts();
|
|
284
|
-
pollTimerRef.current = setInterval(async () => {
|
|
285
|
-
const s = useSyncStore.getState();
|
|
286
|
-
const st = s.sessionStatus[sessionId];
|
|
287
|
-
if (st?.type !== "busy" && st?.type !== "retry") {
|
|
288
|
-
// Session went idle — stop polling
|
|
289
|
-
if (pollTimerRef.current) {
|
|
290
|
-
clearInterval(pollTimerRef.current);
|
|
291
|
-
pollTimerRef.current = null;
|
|
292
|
-
}
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
// Skip fetch if SSE is delivering data (part count grew)
|
|
296
|
-
const currentCount = countParts();
|
|
297
|
-
if (currentCount > lastPartCountRef.current) {
|
|
298
|
-
lastPartCountRef.current = currentCount;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
if (pollInFlightRef.current || pollInFlightSessions.has(sessionId))
|
|
302
|
-
return;
|
|
303
|
-
// SSE appears stalled — fetch messages AND session status
|
|
304
|
-
pollInFlightRef.current = true;
|
|
305
|
-
pollInFlightSessions.add(sessionId);
|
|
306
|
-
try {
|
|
307
|
-
const [msgRes, statusRes] = await Promise.all([
|
|
308
|
-
getClient().session.messages({ sessionID: sessionId }),
|
|
309
|
-
getClient().session.status().catch(() => null),
|
|
310
|
-
]);
|
|
311
|
-
if (msgRes.data) {
|
|
312
|
-
useSyncStore.getState().hydrate(sessionId, msgRes.data);
|
|
313
|
-
}
|
|
314
|
-
// Update session status from server — without this,
|
|
315
|
-
// a dead SSE means session.idle never arrives and the
|
|
316
|
-
// UI stays stuck on "busy" forever.
|
|
317
|
-
if (statusRes?.data) {
|
|
318
|
-
const statuses = statusRes.data;
|
|
319
|
-
const serverStatus = statuses[sessionId];
|
|
320
|
-
if (serverStatus) {
|
|
321
|
-
useSyncStore.getState().setStatus(sessionId, serverStatus);
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
// Session not in busy statuses map → it's idle
|
|
325
|
-
useSyncStore.getState().setStatus(sessionId, { type: "idle" });
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
catch {
|
|
330
|
-
// Silently ignore — will retry on next interval
|
|
331
|
-
}
|
|
332
|
-
finally {
|
|
333
|
-
pollInFlightRef.current = false;
|
|
334
|
-
pollInFlightSessions.delete(sessionId);
|
|
335
|
-
lastPartCountRef.current = countParts();
|
|
336
|
-
}
|
|
337
|
-
}, MESSAGE_POLL_INTERVAL_MS);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
else {
|
|
341
|
-
// Session is idle — stop polling
|
|
342
|
-
if (pollTimerRef.current) {
|
|
343
|
-
clearInterval(pollTimerRef.current);
|
|
344
|
-
pollTimerRef.current = null;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
// Cleanup polling on unmount
|
|
349
|
-
useEffect(() => {
|
|
350
|
-
return () => {
|
|
351
|
-
if (pollTimerRef.current) {
|
|
352
|
-
clearInterval(pollTimerRef.current);
|
|
353
|
-
pollTimerRef.current = null;
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}, []);
|
|
357
|
-
// Single selector that derives MessageWithParts[] with reference caching.
|
|
358
|
-
// The buildMessages function returns the same array reference if nothing
|
|
359
|
-
// relevant to this session changed — preventing unnecessary re-renders.
|
|
360
|
-
const messages = useSyncStore((s) => buildMessages(sessionId, s.messages[sessionId], s.parts));
|
|
361
|
-
const status = useSyncStore((s) => s.sessionStatus[sessionId] ?? IDLE_STATUS);
|
|
362
|
-
const diffs = useSyncStore((s) => s.diffs[sessionId]);
|
|
363
|
-
const todos = useSyncStore((s) => s.todos[sessionId]);
|
|
364
|
-
const isBusy = status?.type === "busy" || status?.type === "retry";
|
|
365
|
-
const isLoading = !useSyncStore((s) => sessionId in s.messages);
|
|
72
|
+
controller.setBusy(runtimeHealthy && isBusy);
|
|
73
|
+
}, [controller, isBusy, runtimeHealthy]);
|
|
366
74
|
return {
|
|
367
75
|
messages,
|
|
368
76
|
status,
|
|
77
|
+
freshness: sync.freshness,
|
|
369
78
|
isBusy,
|
|
370
79
|
isLoading,
|
|
80
|
+
hasOlder: sync.hasOlder,
|
|
81
|
+
isLoadingOlder: sync.isLoadingOlder,
|
|
82
|
+
loadOlder: controller.loadOlder,
|
|
371
83
|
diffs: diffs ?? EMPTY_DIFFS,
|
|
372
84
|
todos: todos ?? EMPTY_TODOS,
|
|
373
85
|
};
|