@beeuni/sdk 0.1.0 → 0.3.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/README.md +53 -92
- package/dist/beeuni.esm.min.js +36 -34
- package/dist/beeuni.global.js +36 -34
- package/dist/browser/cache/idb-sync-cache.js +15 -5
- package/dist/browser/session-sync/server-transcript-mirror.d.ts +7 -1
- package/dist/browser/session-sync/server-transcript-mirror.js +30 -24
- package/dist/browser/session-sync/session-sync-registry.d.ts +3 -1
- package/dist/browser/session-sync/session-sync-registry.js +48 -3
- package/dist/browser/stores/sync-store/keep-unchanged.d.ts +4 -0
- package/dist/browser/stores/sync-store/keep-unchanged.js +34 -0
- package/dist/browser/stores/sync-store.d.ts +7 -0
- package/dist/browser/stores/sync-store.js +122 -35
- package/dist/core/attachments/prompt-attachments.d.ts +4 -0
- package/dist/core/attachments/prompt-attachments.js +34 -0
- package/dist/core/capture/rules.d.ts +17 -0
- package/dist/core/capture/rules.js +27 -0
- package/dist/core/client/beeuni.d.ts +99 -34
- package/dist/core/client/beeuni.js +5 -11
- package/dist/core/client/capture.d.ts +105 -0
- package/dist/core/client/capture.js +48 -0
- package/dist/core/client/facade-catalog.d.ts +0 -4
- package/dist/core/client/facade-catalog.js +1 -5
- package/dist/core/client/facade-identity.d.ts +1 -0
- package/dist/core/client/facade-identity.js +1 -0
- package/dist/core/client/project-access-resources.d.ts +3 -1
- package/dist/core/client/project-access-resources.js +1 -1
- package/dist/core/client/project-connections.d.ts +2 -2
- package/dist/core/client/project-connections.js +2 -2
- package/dist/core/client/project-operations-admin.d.ts +4 -4
- package/dist/core/client/project-operations-review.d.ts +2 -3
- package/dist/core/client/project-operations-review.js +1 -0
- package/dist/core/client/project-operations-workspace.d.ts +4 -4
- package/dist/core/client/project-operations-workspace.js +1 -1
- package/dist/core/client/project-operations.d.ts +9 -19
- package/dist/core/client/project-operations.js +2 -6
- package/dist/core/client/project-platform-security.d.ts +1 -1
- package/dist/core/client/project-platform-settings.d.ts +1 -1
- package/dist/core/client/session-context.d.ts +2 -0
- package/dist/core/client/session-handle.d.ts +13 -0
- package/dist/core/client/session-lifecycle-resources.d.ts +11 -0
- package/dist/core/client/session-lifecycle-resources.js +4 -0
- package/dist/core/client/session-lifecycle-security.d.ts +2 -0
- package/dist/core/client/session-lifecycle-security.js +1 -0
- package/dist/core/client/session-readiness.d.ts +2 -0
- package/dist/core/client/session-readiness.js +20 -1
- package/dist/core/http/api-client.d.ts +1 -7
- package/dist/core/http/api-client.js +2 -37
- package/dist/core/http/auth.d.ts +2 -4
- package/dist/core/http/auth.js +2 -8
- package/dist/core/http/config.d.ts +2 -0
- package/dist/core/http/response-error.js +26 -11
- package/dist/core/http/transport.js +6 -1
- package/dist/core/models/composer-model.d.ts +14 -0
- package/dist/core/models/composer-model.js +21 -1
- package/dist/core/rest/platform-client/capture.d.ts +363 -0
- package/dist/core/rest/platform-client/capture.js +116 -0
- package/dist/core/rest/platform-client/client-policy.d.ts +8 -0
- package/dist/core/rest/platform-client/client-policy.js +30 -0
- package/dist/core/rest/platform-client/index.d.ts +4 -4
- package/dist/core/rest/platform-client/index.js +4 -4
- package/dist/core/rest/platform-client/lifecycle.js +18 -12
- package/dist/core/rest/platform-client/shared.d.ts +5 -0
- package/dist/core/rest/platform-client/shared.js +18 -0
- package/dist/core/rest/platform-client/types.d.ts +1 -1
- package/dist/core/rest/projects-client/account-secret-resources.d.ts +2 -1
- package/dist/core/rest/projects-client/accounts.d.ts +34 -0
- package/dist/core/rest/projects-client/accounts.js +10 -0
- package/dist/core/rest/projects-client/admin-providers.d.ts +0 -61
- package/dist/core/rest/projects-client/admin-providers.js +0 -6
- package/dist/core/rest/projects-client/agent-config.d.ts +3 -0
- package/dist/core/rest/projects-client/apps-fleet.d.ts +5 -1
- package/dist/core/rest/projects-client/apps-fleet.js +2 -2
- package/dist/core/rest/projects-client/apps.d.ts +11 -1
- package/dist/core/rest/projects-client/change-requests.d.ts +2 -0
- package/dist/core/rest/projects-client/change-requests.js +2 -0
- package/dist/core/rest/projects-client/connectors.d.ts +13 -55
- package/dist/core/rest/projects-client/connectors.js +5 -29
- package/dist/core/rest/projects-client/files.d.ts +3 -0
- package/dist/core/rest/projects-client/files.js +4 -0
- package/dist/core/rest/projects-client/gateway.d.ts +5 -0
- package/dist/core/rest/projects-client/git-backend.d.ts +1 -10
- package/dist/core/rest/projects-client/git-backend.js +0 -10
- package/dist/core/rest/projects-client/iam-actions.d.ts +76 -0
- package/dist/core/rest/projects-client/iam-actions.js +101 -0
- package/dist/core/rest/projects-client/iam-sso.d.ts +0 -10
- package/dist/core/rest/projects-client/iam-sso.js +0 -3
- package/dist/core/rest/projects-client/iam.d.ts +1 -0
- package/dist/core/rest/projects-client/iam.js +1 -0
- package/dist/core/rest/projects-client/index.d.ts +1 -4
- package/dist/core/rest/projects-client/index.js +1 -4
- package/dist/core/rest/projects-client/notifications.d.ts +1 -1
- package/dist/core/rest/projects-client/notifications.js +4 -0
- package/dist/core/rest/projects-client/projects.d.ts +4 -2
- package/dist/core/rest/projects-client/projects.js +4 -2
- package/dist/core/rest/projects-client/review-items.d.ts +2 -0
- package/dist/core/rest/projects-client/review-items.js +4 -0
- package/dist/core/rest/projects-client/review.d.ts +17 -3
- package/dist/core/rest/projects-client/review.js +11 -0
- package/dist/core/rest/projects-client/sandbox.d.ts +8 -9
- package/dist/core/rest/projects-client/secrets.d.ts +3 -0
- package/dist/core/rest/projects-client/session-attachments.js +14 -1
- package/dist/core/rest/projects-client/session-sandbox.d.ts +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +31 -4
- package/dist/core/rest/projects-client/sessions.js +34 -4
- package/dist/core/runtime/runtime-rest-client.js +1 -1
- package/dist/core/runtime/runtime-types.d.ts +1 -0
- package/dist/core/session/control-stream.d.ts +1 -0
- package/dist/core/session/control-stream.js +18 -1
- package/dist/core/session/open-bundle.d.ts +4 -4
- package/dist/core/session/open-bundle.js +29 -46
- package/dist/core/session/turn-end-watch.d.ts +47 -0
- package/dist/core/session/turn-end-watch.js +141 -0
- package/dist/core/session-log/guard.d.ts +6 -0
- package/dist/core/session-log/guard.js +109 -0
- package/dist/core/session-log/index.d.ts +7 -0
- package/dist/core/session-log/index.js +5 -0
- package/dist/core/session-log/saved-log.d.ts +10 -0
- package/dist/core/session-log/saved-log.js +25 -0
- package/dist/core/session-log/source.d.ts +25 -0
- package/dist/core/session-log/source.js +30 -0
- package/dist/core/session-log/store.d.ts +21 -0
- package/dist/core/session-log/store.js +126 -0
- package/dist/core/session-log/tool-call.d.ts +11 -0
- package/dist/core/session-log/tool-call.js +65 -0
- package/dist/core/session-log/turns/blocks.d.ts +60 -0
- package/dist/core/session-log/turns/blocks.js +124 -0
- package/dist/core/session-log/turns/history.d.ts +32 -0
- package/dist/core/session-log/turns/history.js +99 -0
- package/dist/core/session-log/turns/index.d.ts +4 -0
- package/dist/core/session-log/turns/index.js +4 -0
- package/dist/core/session-log/turns/metrics.d.ts +21 -0
- package/dist/core/session-log/turns/metrics.js +29 -0
- package/dist/core/session-log/turns/turn.d.ts +55 -0
- package/dist/core/session-log/turns/turn.js +279 -0
- package/dist/core/session-log/turns/v1-parts.d.ts +8 -0
- package/dist/core/session-log/turns/v1-parts.js +43 -0
- package/dist/core/session-log/types.d.ts +264 -0
- package/dist/core/session-log/types.js +6 -0
- package/dist/core/session-log/v1-view.d.ts +13 -0
- package/dist/core/session-log/v1-view.js +215 -0
- package/dist/core/session-log/v1.d.ts +31 -0
- package/dist/core/session-log/v1.js +473 -0
- package/dist/core/session-sync/copy-store-types.d.ts +19 -0
- package/dist/core/session-sync/copy-store-types.js +1 -0
- package/dist/core/session-sync/saved-copy-store.js +25 -4
- package/dist/core/session-sync/saved-transcript.d.ts +15 -1
- package/dist/core/session-sync/saved-transcript.js +55 -0
- package/dist/core/session-sync/session-sync-controller.js +3 -0
- package/dist/core/stream/event-stream.js +2 -2
- package/dist/core/turns/classify.d.ts +2 -1
- package/dist/core/turns/classify.js +7 -3
- package/dist/core/turns/edit-blocks.d.ts +1 -0
- package/dist/core/turns/edit-blocks.js +15 -0
- package/dist/core/turns/parts.d.ts +3 -2
- package/dist/core/turns/parts.js +4 -2
- package/dist/core/turns/segments/burst-summary.d.ts +1 -0
- package/dist/core/turns/segments/burst-summary.js +24 -0
- package/dist/core/turns/segments/group-steps.d.ts +4 -3
- package/dist/core/turns/segments/group-steps.js +9 -6
- package/dist/core/turns/segments/index.d.ts +1 -0
- package/dist/core/turns/segments/index.js +1 -0
- package/dist/core/turns/segments/narration.d.ts +5 -4
- package/dist/core/turns/segments/narration.js +12 -3
- package/dist/core/turns/segments/session-activity-groups.d.ts +2 -1
- package/dist/core/turns/segments/session-activity-groups.js +3 -1
- package/dist/core/turns/segments/step-label.d.ts +2 -1
- package/dist/core/turns/segments/step-label.js +3 -1
- package/dist/core/turns/segments/work-split.d.ts +10 -0
- package/dist/core/turns/segments/work-split.js +40 -0
- package/dist/core/turns/segments/working-turn.d.ts +4 -2
- package/dist/core/turns/segments/working-turn.js +9 -8
- package/dist/core/turns/state.d.ts +2 -7
- package/dist/core/turns/state.js +28 -134
- package/dist/core/turns/tool-kind.d.ts +1 -1
- package/dist/core/turns/tool-kind.js +3 -2
- package/dist/core/turns/tool-registry.js +1 -0
- package/dist/core/turns/tools/tool-meta.d.ts +3 -2
- package/dist/core/turns/tools/tool-meta.js +5 -2
- package/dist/core/turns/tools/tool-outcome.d.ts +2 -1
- package/dist/core/turns/tools/tool-outcome.js +3 -1
- package/dist/core/turns/types.d.ts +1 -1
- package/dist/core/turns/view-model.js +1 -13
- package/dist/core/warm-session-pool.d.ts +29 -0
- package/dist/core/warm-session-pool.js +127 -0
- package/dist/genui/catalog.d.ts +28 -0
- package/dist/genui/catalog.js +396 -0
- package/dist/genui/expansion.d.ts +3 -0
- package/dist/genui/expansion.js +295 -0
- package/dist/genui/fence-entry.d.ts +2 -0
- package/dist/genui/fence-entry.js +1 -0
- package/dist/genui/fence.d.ts +21 -0
- package/dist/genui/fence.js +138 -0
- package/dist/genui/index.d.ts +6 -0
- package/dist/genui/index.js +6 -0
- package/dist/genui/markdown.d.ts +12 -0
- package/dist/genui/markdown.js +104 -0
- package/dist/genui/parse.d.ts +6 -0
- package/dist/genui/parse.js +75 -0
- package/dist/genui/prompt.d.ts +2 -0
- package/dist/genui/prompt.js +206 -0
- package/dist/genui/react/default-components.d.ts +2 -0
- package/dist/genui/react/default-components.js +72 -0
- package/dist/genui/react/default-components.test.d.ts +1 -0
- package/dist/genui/react/default-components.test.js +94 -0
- package/dist/genui/react/genui-block-disabled.test.d.ts +1 -0
- package/dist/genui/react/genui-block-disabled.test.js +50 -0
- package/dist/genui/react/genui-block.d.ts +28 -0
- package/dist/genui/react/genui-block.js +91 -0
- package/dist/genui/react/genui-block.test.d.ts +1 -0
- package/dist/genui/react/genui-block.test.js +205 -0
- package/dist/genui/react/index.d.ts +2 -0
- package/dist/genui/react/index.js +2 -0
- package/dist/genui/share.d.ts +5 -0
- package/dist/genui/share.js +30 -0
- package/dist/genui/test-fixtures.d.ts +2 -0
- package/dist/genui/test-fixtures.js +16 -0
- package/dist/genui/types.d.ts +29 -0
- package/dist/genui/types.js +1 -0
- package/dist/genui/urls.d.ts +1 -0
- package/dist/genui/urls.js +18 -0
- package/dist/genui/validate.d.ts +9 -0
- package/dist/genui/validate.js +116 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/connector-catalog.d.ts +48 -0
- package/dist/internal/connector-catalog.js +27 -0
- package/dist/internal/host-configuration.d.ts +22 -0
- package/dist/internal/host-configuration.js +28 -0
- package/dist/internal/platform-admin-react.d.ts +12 -0
- package/dist/internal/platform-admin-react.js +13 -0
- package/dist/internal/platform-admin.d.ts +9 -0
- package/dist/internal/platform-admin.js +9 -0
- package/dist/platform/ui.d.ts +8 -2
- package/dist/platform/ui.js +19 -4
- package/dist/react/chat/use-chat-turns.d.ts +1 -1
- package/dist/react/human-presence.d.ts +9 -0
- package/dist/react/human-presence.js +12 -0
- package/dist/react/index.d.ts +4 -13
- package/dist/react/index.js +4 -13
- package/dist/react/opencode.d.ts +1 -0
- package/dist/react/opencode.js +1 -0
- package/dist/react/query-contracts.d.ts +6 -0
- package/dist/react/query-contracts.js +6 -0
- package/dist/react/query-keys.d.ts +17 -2
- package/dist/react/query-keys.js +24 -2
- package/dist/react/reset-identity-state.js +2 -0
- package/dist/react/session-cache-write.d.ts +1 -0
- package/dist/react/session-cache-write.js +20 -0
- package/dist/react/session-transcript-subscription.js +3 -1
- package/dist/react/sign-in-with-beeuni.d.ts +6 -6
- package/dist/react/turn-end-handoff.d.ts +13 -0
- package/dist/react/turn-end-handoff.js +101 -0
- package/dist/react/use-admin-providers.d.ts +3 -6
- package/dist/react/use-admin-providers.js +1 -11
- package/dist/react/use-capture.d.ts +47 -0
- package/dist/react/use-capture.js +173 -0
- package/dist/react/use-feature-flag.js +1 -0
- package/dist/react/use-opencode-events/handle-event.d.ts +1 -0
- package/dist/react/use-opencode-events/handle-event.js +5 -0
- package/dist/react/use-opencode-events/handle-interaction-event.js +8 -3
- package/dist/react/use-opencode-events/handle-session-event.js +13 -8
- package/dist/react/use-opencode-events/handler-context.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.js +11 -1
- package/dist/react/use-opencode-events/index.js +1 -0
- package/dist/react/use-opencode-local.d.ts +5 -1
- package/dist/react/use-opencode-local.js +15 -2
- package/dist/react/use-opencode-sessions/commands.d.ts +11 -1
- package/dist/react/use-opencode-sessions/commands.js +21 -1
- package/dist/react/use-opencode-sessions/permissions.js +28 -1
- package/dist/react/use-project-sessions.d.ts +1 -1
- package/dist/react/use-project-sessions.js +6 -5
- package/dist/react/use-prompt-attachments.js +1 -0
- package/dist/react/use-review-summary.d.ts +13 -0
- package/dist/react/use-review-summary.js +32 -0
- package/dist/react/use-runtime-reconnect.js +1 -1
- package/dist/react/use-session-list-state.d.ts +6 -0
- package/dist/react/use-session-list-state.js +33 -0
- package/dist/react/use-session-log.d.ts +16 -0
- package/dist/react/use-session-log.js +143 -0
- package/dist/react/use-session-messages.d.ts +2 -1
- package/dist/react/use-session-messages.js +15 -14
- package/dist/react/use-session-stream.js +12 -1
- package/dist/react/use-session-sync.d.ts +2 -2
- package/dist/react/use-session-sync.js +10 -8
- package/dist/react/use-session-transcript-history.js +2 -12
- package/dist/react/use-session-working.d.ts +2 -0
- package/dist/react/use-session-working.js +10 -0
- package/dist/react/use-session.d.ts +6 -0
- package/dist/react/use-session.js +88 -6
- package/package.json +62 -8
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-store.js';
|
|
4
|
+
import { useSyncStore } from '../browser/stores/sync-store.js';
|
|
5
|
+
import { openSessionStream } from '../core/session/control-stream.js';
|
|
6
|
+
import { savedSessionLogCopies } from '../core/session-log/saved-log.js';
|
|
7
|
+
import { sessionLogOfV1Rows, sessionLogThreadWithV1Rows } from '../core/session-log/v1-view.js';
|
|
8
|
+
import { fetchFirstSessionLogPage, loadSessionLogPage, negotiateSessionLog, } from '../core/session-log/source.js';
|
|
9
|
+
import { currentSavedCopyStore } from '../core/session-sync/saved-copy-store.js';
|
|
10
|
+
import { selectSessionLog, selectSessionLogThread } from '../core/session-log/store.js';
|
|
11
|
+
import { useFeatureFlag } from './use-feature-flag.js';
|
|
12
|
+
import { selectSessionRows } from './session-transcript-subscription.js';
|
|
13
|
+
import { usePacedStoreValue, useSessionMessages } from './use-session-messages.js';
|
|
14
|
+
const PACED = Symbol('paced');
|
|
15
|
+
const sourced = new WeakMap();
|
|
16
|
+
const withSource = (log, source) => {
|
|
17
|
+
let out = sourced.get(log);
|
|
18
|
+
if (!out)
|
|
19
|
+
sourced.set(log, (out = { ...log, source }));
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
function useV1SessionLog(session, threadId, enabled, options) {
|
|
23
|
+
const native = enabled ? threadId || session.runtimeSessionId || session.opencodeSessionId || null : null;
|
|
24
|
+
const rows = useSessionMessages({ ...session, runtimeSessionId: native, opencodeSessionId: null }, options);
|
|
25
|
+
return useMemo(() => (native && rows.length ? sessionLogOfV1Rows(session.sessionId, native, rows) : null), [session.sessionId, native, rows]);
|
|
26
|
+
}
|
|
27
|
+
export function useSessionLog(session) {
|
|
28
|
+
const v2 = useSyncStore((state) => {
|
|
29
|
+
const log = state.sessionLogs[session.sessionId];
|
|
30
|
+
return log ? selectSessionLog(log) : null;
|
|
31
|
+
});
|
|
32
|
+
const v1 = useV1SessionLog(session, undefined, !v2);
|
|
33
|
+
return v2 ? withSource(v2, 'v2') : v1 ? withSource(v1, 'v1') : null;
|
|
34
|
+
}
|
|
35
|
+
export function useSessionLogRecord(session) {
|
|
36
|
+
return useSyncStore((state) => state.sessionLogs[session.sessionId]?.session ?? null);
|
|
37
|
+
}
|
|
38
|
+
export function useSessionLogThread(session, threadId, options) {
|
|
39
|
+
const throttleMs = options?.throttleMs && options.throttleMs > 0 ? options.throttleMs : 0;
|
|
40
|
+
const select = (state) => {
|
|
41
|
+
const log = state.sessionLogs[session.sessionId];
|
|
42
|
+
if (!log)
|
|
43
|
+
return undefined;
|
|
44
|
+
const thread = selectSessionLogThread(log, threadId);
|
|
45
|
+
return thread && sessionLogThreadWithV1Rows(log, thread, selectSessionRows(state, thread.thread_id), (id) => state.optimisticEchoOf(thread.thread_id, id));
|
|
46
|
+
};
|
|
47
|
+
const live = useSyncStore((state) => (throttleMs ? PACED : select(state)));
|
|
48
|
+
const paced = usePacedStoreValue(select, `${session.sessionId}/${threadId ?? ''}`, throttleMs);
|
|
49
|
+
const v2 = live === PACED ? paced : live;
|
|
50
|
+
const v1 = useV1SessionLog(session, threadId, v2 === undefined, options);
|
|
51
|
+
return v2 !== undefined ? v2 : (v1?.threads[0] ?? null);
|
|
52
|
+
}
|
|
53
|
+
export function useSessionLogSync(projectId, sessionId, options) {
|
|
54
|
+
const loader = options.loader === undefined ? loadSessionLogPage : options.loader;
|
|
55
|
+
const joinStream = options.joinStream ?? openSessionStream;
|
|
56
|
+
const active = options.enabled && !!loader && !!projectId && !!sessionId;
|
|
57
|
+
const live = useSandboxConnectionStore((state) => state.runtimeCapabilities);
|
|
58
|
+
const served = useRef(null);
|
|
59
|
+
const key = `${projectId}/${sessionId}`;
|
|
60
|
+
if (live?.includes('session.log.v2'))
|
|
61
|
+
served.current = key;
|
|
62
|
+
const capabilities = served.current === key ? ['session.log.v2'] : live;
|
|
63
|
+
const flag = useFeatureFlag(active ? projectId : null, 'session_log').enabled;
|
|
64
|
+
const negotiated = active ? negotiateSessionLog({ capabilities, flag, loader }) : 'v1';
|
|
65
|
+
const [refusedKey, setRefusedKey] = useState(null);
|
|
66
|
+
const dialect = negotiated === 'v2' && refusedKey !== key ? 'v2' : 'v1';
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (negotiated !== 'v2' || !loader)
|
|
69
|
+
return;
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const store = useSyncStore.getState;
|
|
72
|
+
const copies = savedSessionLogCopies(currentSavedCopyStore());
|
|
73
|
+
let stream = null;
|
|
74
|
+
let stopped = false;
|
|
75
|
+
let busy = false;
|
|
76
|
+
let lastEnded;
|
|
77
|
+
const join = (options) => {
|
|
78
|
+
stream?.close();
|
|
79
|
+
stream = joinStream({ projectId, sessionId, ...options });
|
|
80
|
+
};
|
|
81
|
+
const load = async () => {
|
|
82
|
+
const page = await fetchFirstSessionLogPage(loader, projectId, sessionId, controller.signal);
|
|
83
|
+
if (stopped)
|
|
84
|
+
return null;
|
|
85
|
+
if (page) {
|
|
86
|
+
store().applySessionLogPage(sessionId, page);
|
|
87
|
+
void copies?.write(projectId, sessionId, page);
|
|
88
|
+
}
|
|
89
|
+
return page;
|
|
90
|
+
};
|
|
91
|
+
const onReconnect = (run) => {
|
|
92
|
+
let connected = true;
|
|
93
|
+
return (up) => {
|
|
94
|
+
if (up && !connected)
|
|
95
|
+
run();
|
|
96
|
+
connected = up;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const attempt = async () => {
|
|
100
|
+
if (busy || stopped)
|
|
101
|
+
return;
|
|
102
|
+
busy = true;
|
|
103
|
+
const page = await load();
|
|
104
|
+
busy = false;
|
|
105
|
+
if (stopped)
|
|
106
|
+
return;
|
|
107
|
+
if (page) {
|
|
108
|
+
setRefusedKey(null);
|
|
109
|
+
join({ schema: 'v2', onConnectionChange: onReconnect(() => void load()), onRuntimeResync: () => void load() });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
store().dropSessionLog(sessionId);
|
|
113
|
+
setRefusedKey(`${projectId}/${sessionId}`);
|
|
114
|
+
join({
|
|
115
|
+
onControl: (frame) => {
|
|
116
|
+
if (frame.type !== 'beeuni.control.turn')
|
|
117
|
+
return;
|
|
118
|
+
const ended = frame.payload?.last_ended?.turn_token ?? null;
|
|
119
|
+
const seen = lastEnded;
|
|
120
|
+
lastEnded = ended;
|
|
121
|
+
if (seen !== undefined && ended !== seen)
|
|
122
|
+
void attempt();
|
|
123
|
+
},
|
|
124
|
+
onConnectionChange: onReconnect(() => void attempt()),
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
void (async () => {
|
|
128
|
+
const saved = await Promise.resolve(copies?.read(projectId, sessionId) ?? null).catch(() => null);
|
|
129
|
+
if (stopped)
|
|
130
|
+
return;
|
|
131
|
+
if (saved && !store().sessionLogs[sessionId])
|
|
132
|
+
store().applySessionLogPage(sessionId, saved);
|
|
133
|
+
await attempt();
|
|
134
|
+
})();
|
|
135
|
+
return () => {
|
|
136
|
+
stopped = true;
|
|
137
|
+
controller.abort();
|
|
138
|
+
stream?.close();
|
|
139
|
+
store().dropSessionLog(sessionId);
|
|
140
|
+
};
|
|
141
|
+
}, [negotiated, loader, joinStream, projectId, sessionId]);
|
|
142
|
+
return dialect;
|
|
143
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type MessageWithParts } from '../browser/stores/sync-store.js';
|
|
1
|
+
import { useSyncStore, type MessageWithParts } from '../browser/stores/sync-store.js';
|
|
2
2
|
export interface SessionMessagesSource {
|
|
3
3
|
projectId: string;
|
|
4
4
|
sessionId: string;
|
|
@@ -9,3 +9,4 @@ export interface SessionMessagesOptions {
|
|
|
9
9
|
throttleMs?: number;
|
|
10
10
|
}
|
|
11
11
|
export declare function useSessionMessages(session: SessionMessagesSource, options?: SessionMessagesOptions): MessageWithParts[];
|
|
12
|
+
export declare function usePacedStoreValue<T>(read: (state: ReturnType<typeof useSyncStore.getState>) => T, key: string, throttleMs: number): T;
|
|
@@ -16,31 +16,32 @@ export function useSessionMessages(session, options = {}) {
|
|
|
16
16
|
return useMemo(() => messagesBeforeRewind(rows, rewind), [rows, rewind]);
|
|
17
17
|
}
|
|
18
18
|
function usePacedRows(readableSessionId, throttleMs) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
return usePacedStoreValue((state) => (readableSessionId ? selectSessionRows(state, readableSessionId) : EMPTY_ROWS), readableSessionId, throttleMs);
|
|
20
|
+
}
|
|
21
|
+
export function usePacedStoreValue(read, key, throttleMs) {
|
|
22
|
+
const current = () => read(useSyncStore.getState());
|
|
23
|
+
const [state, setState] = useState(() => ({ key, value: current() }));
|
|
21
24
|
useEffect(() => {
|
|
22
25
|
if (!throttleMs)
|
|
23
26
|
return;
|
|
24
27
|
let lastShownAt = -Infinity;
|
|
25
28
|
let timer = null;
|
|
26
|
-
let shown
|
|
29
|
+
let shown;
|
|
27
30
|
const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
28
31
|
const flush = () => {
|
|
29
32
|
timer = null;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
33
|
+
const value = current();
|
|
34
|
+
if (value === shown)
|
|
32
35
|
return;
|
|
33
|
-
shown =
|
|
36
|
+
shown = value;
|
|
34
37
|
lastShownAt = now();
|
|
35
|
-
setState({
|
|
38
|
+
setState({ key, value });
|
|
36
39
|
};
|
|
37
|
-
const initial =
|
|
40
|
+
const initial = current();
|
|
38
41
|
shown = initial;
|
|
39
|
-
setState((prev) => prev.
|
|
40
|
-
? prev
|
|
41
|
-
: { id: readableSessionId, rows: initial });
|
|
42
|
+
setState((prev) => (prev.key === key && prev.value === initial ? prev : { key, value: initial }));
|
|
42
43
|
const unsubscribe = useSyncStore.subscribe(() => {
|
|
43
|
-
if (
|
|
44
|
+
if (current() === shown)
|
|
44
45
|
return;
|
|
45
46
|
const wait = throttleMs - (now() - lastShownAt);
|
|
46
47
|
if (wait <= 0) {
|
|
@@ -57,6 +58,6 @@ function usePacedRows(readableSessionId, throttleMs) {
|
|
|
57
58
|
if (timer !== null)
|
|
58
59
|
clearTimeout(timer);
|
|
59
60
|
};
|
|
60
|
-
}, [
|
|
61
|
-
return state.
|
|
61
|
+
}, [key, throttleMs]);
|
|
62
|
+
return state.key === key ? state.value : current();
|
|
62
63
|
}
|
|
@@ -10,19 +10,30 @@ import { qk } from './query-keys.js';
|
|
|
10
10
|
export function useSessionStreamConnected(projectId, sessionId) {
|
|
11
11
|
return useSyncExternalStore(subscribeSessionStreamConnections, () => !!projectId && !!sessionId && sessionStreamConnected(projectId, sessionId), () => false);
|
|
12
12
|
}
|
|
13
|
+
function openTurnsKey(turns) {
|
|
14
|
+
return (turns ?? []).map((turn) => turn.turn_token).sort().join(',');
|
|
15
|
+
}
|
|
13
16
|
export function applySessionControlFrame(queryClient, projectId, sessionId, frame, memory, nowMs = Date.now()) {
|
|
14
17
|
if (frame.type === 'beeuni.control.turn') {
|
|
15
18
|
const payload = frame.payload;
|
|
16
19
|
if (!payload?.known || !Array.isArray(payload.turns))
|
|
17
20
|
return;
|
|
21
|
+
const key = qk.project.sessionTurn(projectId, sessionId);
|
|
22
|
+
const previous = queryClient.getQueryData(key);
|
|
23
|
+
const canStop = typeof payload.can_stop === 'boolean'
|
|
24
|
+
? payload.can_stop
|
|
25
|
+
: previous && openTurnsKey(previous.turns) === openTurnsKey(payload.turns)
|
|
26
|
+
? previous.can_stop
|
|
27
|
+
: undefined;
|
|
18
28
|
const observation = {
|
|
19
29
|
turns: payload.turns,
|
|
20
30
|
...(payload.last_ended ? { last_ended: payload.last_ended } : {}),
|
|
21
31
|
...(payload.recent_failures ? { recent_failures: payload.recent_failures } : {}),
|
|
32
|
+
...(canStop !== undefined ? { can_stop: canStop } : {}),
|
|
22
33
|
...(payload.working ? { working: payload.working } : {}),
|
|
23
34
|
atMs: nowMs,
|
|
24
35
|
};
|
|
25
|
-
queryClient.setQueryData(
|
|
36
|
+
queryClient.setQueryData(key, observation);
|
|
26
37
|
return;
|
|
27
38
|
}
|
|
28
39
|
if (frame.type === 'beeuni.control.session') {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SessionTranscriptSyncEnvelope } from '../core/rest/projects-client/sessions.js';
|
|
2
|
-
import { loadSessionRuntimeStatus, loadSessionTranscriptMessages } from '../browser/session-sync/session-sync-registry.js';
|
|
3
|
-
export { loadSessionRuntimeStatus, loadSessionTranscriptMessages };
|
|
2
|
+
import { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages } from '../browser/session-sync/session-sync-registry.js';
|
|
3
|
+
export { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages };
|
|
4
4
|
type FileDiff = Omit<import('../core/runtime/runtime-types.js').SnapshotFileDiff, 'patch'> & {
|
|
5
5
|
patch?: string;
|
|
6
6
|
before?: string;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { claimSessionCacheOwnership, getSessionCacheOwnership, resolveSessionCacheOwnerScope, sessionCacheOwnerScopesConflict, } from '../browser/session-sync/session-cache-ownership.js';
|
|
4
|
-
import { getSessionSyncController, loadSessionRuntimeStatus, loadSessionTranscriptMessages, resetSessionSyncControllersForSession, retainSessionSyncController, } from '../browser/session-sync/session-sync-registry.js';
|
|
4
|
+
import { getSessionSyncController, loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages, resetSessionSyncControllersForSession, retainSessionSyncController, } from '../browser/session-sync/session-sync-registry.js';
|
|
5
5
|
import { loadOlderSessionTranscriptMirror, loadSessionTranscriptMirror, mirrorMessagesForHydrate, parseBeeuniSessionScope, refreshSavedCopy, SAVED_COPY_REFRESH_DELAY_MS, shouldHydrateFromMirror, } from '../browser/session-sync/server-transcript-mirror.js';
|
|
6
6
|
import { currentSavedCopyStore } from '../core/session-sync/saved-copy-store.js';
|
|
7
7
|
import { chooseOlderSource, mirrorCursorAfter } from '../browser/session-sync/mirror-paging.js';
|
|
8
8
|
import { transcriptIsFragment } from '../core/session-sync/fragment.js';
|
|
9
9
|
import { onTabVisible } from '../browser/session-sync/visibility.js';
|
|
10
10
|
import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-store.js';
|
|
11
|
-
import { hasOnlyCacheSourcedMessages, useSyncStore } from '../browser/stores/sync-store.js';
|
|
11
|
+
import { hasOnlyCacheSourcedMessages, hasRuntimeConfirmedMessage, useSyncStore } from '../browser/stores/sync-store.js';
|
|
12
12
|
import { useCurrentRuntime } from './use-current-runtime.js';
|
|
13
13
|
import { selectSessionRows } from './session-transcript-subscription.js';
|
|
14
14
|
import { canQueryRuntimeSession } from './use-opencode-sessions/index.js';
|
|
15
|
-
export { loadSessionRuntimeStatus, loadSessionTranscriptMessages };
|
|
15
|
+
export { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages };
|
|
16
16
|
const EMPTY_DIFFS = [];
|
|
17
17
|
const EMPTY_TODOS = [];
|
|
18
18
|
const IDLE_STATUS = { type: 'idle' };
|
|
@@ -197,21 +197,23 @@ export function useSessionSync(sessionId, options = {}) {
|
|
|
197
197
|
setIsLoadingOlderMirror(false);
|
|
198
198
|
mirrorPageInFlight.current = null;
|
|
199
199
|
}, [beeuniSessionScope, sessionId]);
|
|
200
|
+
const savedCursorCovered = useSyncStore(() => !!mirrorCursor && !sync.hasOlder && hasRuntimeConfirmedMessage(sessionId, mirrorCursor));
|
|
201
|
+
const savedCursor = savedCursorCovered ? null : mirrorCursor;
|
|
200
202
|
const loadOlder = useCallback(() => {
|
|
201
203
|
const source = chooseOlderSource({
|
|
202
204
|
runtimeHasOlder: sync.hasOlder,
|
|
203
|
-
mirrorCursor,
|
|
205
|
+
mirrorCursor: savedCursor,
|
|
204
206
|
});
|
|
205
207
|
if (source === 'runtime')
|
|
206
208
|
return controller.loadOlder();
|
|
207
|
-
if (source !== 'mirror' || !
|
|
209
|
+
if (source !== 'mirror' || !savedCursor)
|
|
208
210
|
return Promise.resolve();
|
|
209
211
|
if (mirrorPageInFlight.current)
|
|
210
212
|
return mirrorPageInFlight.current;
|
|
211
213
|
setIsLoadingOlderMirror(true);
|
|
212
214
|
const pending = loadOlderSessionTranscriptMirror({
|
|
213
215
|
beeuniSessionScope,
|
|
214
|
-
before:
|
|
216
|
+
before: savedCursor,
|
|
215
217
|
child: savedChild ? sessionId : undefined,
|
|
216
218
|
})
|
|
217
219
|
.then((envelope) => {
|
|
@@ -231,7 +233,7 @@ export function useSessionSync(sessionId, options = {}) {
|
|
|
231
233
|
});
|
|
232
234
|
mirrorPageInFlight.current = pending;
|
|
233
235
|
return pending;
|
|
234
|
-
}, [controller, beeuniSessionScope,
|
|
236
|
+
}, [controller, beeuniSessionScope, savedCursor, savedChild, sessionId, sync.hasOlder]);
|
|
235
237
|
const retryTranscript = useCallback(() => {
|
|
236
238
|
void controller.reconcile('manual');
|
|
237
239
|
}, [controller]);
|
|
@@ -243,7 +245,7 @@ export function useSessionSync(sessionId, options = {}) {
|
|
|
243
245
|
isBusy,
|
|
244
246
|
isLoading,
|
|
245
247
|
mirrorState,
|
|
246
|
-
hasOlder: sync.hasOlder || Boolean(
|
|
248
|
+
hasOlder: sync.hasOlder || Boolean(savedCursor),
|
|
247
249
|
isLoadingOlder: sync.isLoadingOlder || isLoadingOlderMirror,
|
|
248
250
|
loadOlder,
|
|
249
251
|
diffs: diffs ?? EMPTY_DIFFS,
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useQuery } from '@tanstack/react-query';
|
|
3
|
-
import {
|
|
4
|
-
import { OPEN_BUNDLE_TRANSCRIPT_LIMIT, openBundleHistory, settledOpenBundle, } from '../core/session/open-bundle.js';
|
|
5
|
-
import { savedCopyEmptyRoot } from '../core/session-sync/saved-transcript.js';
|
|
3
|
+
import { loadSavedTranscript, savedCopyEmptyRoot } from '../core/session-sync/saved-transcript.js';
|
|
6
4
|
import { qk } from './query-keys.js';
|
|
7
5
|
export function useSessionTranscriptHistory(projectId, sessionId, enabled) {
|
|
8
6
|
const query = useQuery({
|
|
9
7
|
queryKey: [...qk.project.session(projectId, sessionId), 'transcript-history'],
|
|
10
|
-
queryFn: ({ signal }) => {
|
|
11
|
-
const bundle = settledOpenBundle(projectId, sessionId);
|
|
12
|
-
return ((bundle && openBundleHistory(bundle)) ??
|
|
13
|
-
getSessionTranscriptSync(projectId, sessionId, {
|
|
14
|
-
limit: OPEN_BUNDLE_TRANSCRIPT_LIMIT,
|
|
15
|
-
history: true,
|
|
16
|
-
signal,
|
|
17
|
-
}));
|
|
18
|
-
},
|
|
8
|
+
queryFn: ({ signal }) => loadSavedTranscript(projectId, sessionId, { signal }),
|
|
19
9
|
enabled: enabled && !!projectId && !!sessionId,
|
|
20
10
|
staleTime: 0,
|
|
21
11
|
gcTime: 0,
|
|
@@ -11,6 +11,7 @@ export interface SessionTurnObservation {
|
|
|
11
11
|
turns: SessionTurn[];
|
|
12
12
|
last_ended?: SessionTurnEnded;
|
|
13
13
|
recent_failures?: SessionTurnFailure[];
|
|
14
|
+
can_stop?: boolean;
|
|
14
15
|
working?: SessionWorkingVerdict;
|
|
15
16
|
atMs: number;
|
|
16
17
|
}
|
|
@@ -35,3 +36,4 @@ export declare function readSessionTurnObservation(projectId: string, sessionId:
|
|
|
35
36
|
}): Promise<SessionTurnObservation>;
|
|
36
37
|
export declare function useSessionWorking(projectId: string, sessionId: string, options?: UseSessionWorkingOptions): WorkingProjection;
|
|
37
38
|
export declare function useSessionTurnOutcome(projectId: string, sessionId: string): SessionTurnOutcome;
|
|
39
|
+
export declare function useSessionCanStop(projectId: string, sessionId: string): boolean;
|
|
@@ -59,6 +59,7 @@ export async function readSessionTurnObservation(projectId, sessionId, options =
|
|
|
59
59
|
turns: turn.turns,
|
|
60
60
|
last_ended: turn.last_ended,
|
|
61
61
|
recent_failures: turn.recent_failures,
|
|
62
|
+
...(turn.can_stop !== undefined ? { can_stop: turn.can_stop } : {}),
|
|
62
63
|
atMs: turn.atMs,
|
|
63
64
|
};
|
|
64
65
|
}
|
|
@@ -70,6 +71,7 @@ export async function readSessionTurnObservation(projectId, sessionId, options =
|
|
|
70
71
|
turns: status.turns ?? [],
|
|
71
72
|
last_ended: status.last_ended,
|
|
72
73
|
recent_failures: status.recent_failures,
|
|
74
|
+
...(typeof status.can_stop === 'boolean' ? { can_stop: status.can_stop } : {}),
|
|
73
75
|
atMs,
|
|
74
76
|
};
|
|
75
77
|
});
|
|
@@ -215,3 +217,11 @@ export function useSessionTurnOutcome(projectId, sessionId) {
|
|
|
215
217
|
}, [settleInMs, queryClient, projectId, sessionId]);
|
|
216
218
|
return useMemo(() => ({ last_ended: lastEnded, recent_failures: recentFailures, atMs }), [lastEnded, recentFailures, atMs]);
|
|
217
219
|
}
|
|
220
|
+
export function useSessionCanStop(projectId, sessionId) {
|
|
221
|
+
const query = useQuery({
|
|
222
|
+
queryKey: qk.project.sessionTurn(projectId, sessionId),
|
|
223
|
+
queryFn: () => readSessionTurnObservation(projectId, sessionId, { bundle: false }),
|
|
224
|
+
enabled: false,
|
|
225
|
+
});
|
|
226
|
+
return query.data?.can_stop !== false;
|
|
227
|
+
}
|
|
@@ -6,6 +6,7 @@ import { type AbortSettlement, type PromptPart } from './use-opencode-sessions/i
|
|
|
6
6
|
import { type SessionPicks } from './use-session-picks.js';
|
|
7
7
|
export type SessionPhase = 'starting' | 'ready' | 'error';
|
|
8
8
|
export declare function shouldRetrySessionStart(failureCount: number, error: unknown, sessionId: string): boolean;
|
|
9
|
+
export declare function presenceReturnReopensSession(heldByThisTab: boolean, cachedStage: string | null | undefined): boolean;
|
|
9
10
|
export declare const SESSION_START_POLL_MS = 1500;
|
|
10
11
|
export declare function resolveSessionRuntimeUrl(start: Pick<SessionStartResult, 'stage' | 'sandbox' | 'runtime_url'> | null | undefined): string | null;
|
|
11
12
|
export interface SendOptions {
|
|
@@ -153,6 +154,11 @@ export declare function useSession(projectId: string, sessionId: string, options
|
|
|
153
154
|
runtimePhase: import("./use-runtime-phase.js").RuntimePhase;
|
|
154
155
|
working: import("../index.js").WorkingProjection;
|
|
155
156
|
isBusy: boolean;
|
|
157
|
+
canStop: boolean;
|
|
158
|
+
presenceAwake: {
|
|
159
|
+
until: Date;
|
|
160
|
+
capAt: Date;
|
|
161
|
+
} | null;
|
|
156
162
|
isCompacting: boolean;
|
|
157
163
|
isLoading: boolean;
|
|
158
164
|
savedTranscript: import("../core/session-sync/saved-transcript.js").SavedTranscript;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { useOpenCodeCompactionStore } from '../browser/stores/opencode-compaction-store.js';
|
|
5
5
|
import { useSessionWorkingStore } from '../browser/stores/session-working-store.js';
|
|
6
6
|
import { compactionExpiryAtMs, projectCompacting, serverCompactionRevalidateAtMs, } from '../core/session/compaction.js';
|
|
@@ -29,7 +29,7 @@ import { useCanonicalRuntimeSession } from './use-canonical-opencode-session.js'
|
|
|
29
29
|
import { useRuntimeEventStream } from './use-opencode-events/index.js';
|
|
30
30
|
import { useSessionStream } from './use-session-stream.js';
|
|
31
31
|
import { sessionStreamConnected } from '../core/session/control-stream.js';
|
|
32
|
-
import { presenceReporter, watchHumanPresence } from './human-presence.js';
|
|
32
|
+
import { PRESENCE_AWAKE_REFRESH_MS, presenceAwakeFrom, presenceReporter, watchHumanPresence, } from './human-presence.js';
|
|
33
33
|
import { formatModelString } from './use-opencode-local.js';
|
|
34
34
|
import { runtimeKeys, rejectQuestion as rejectQuestionApi, replyToPermission, replyToQuestion, useAbortRuntimeSession, useExecuteRuntimeCommand, useRuntimeSession, } from './use-opencode-sessions/index.js';
|
|
35
35
|
import { mintSessionWireMessageId } from './use-opencode-sessions/messages.js';
|
|
@@ -42,11 +42,13 @@ import { useRuntimePhase } from './use-runtime-phase.js';
|
|
|
42
42
|
import { useSessionPicks } from './use-session-picks.js';
|
|
43
43
|
import { derivePhase } from './use-session-phase.js';
|
|
44
44
|
import { isEmptyConversation, resolveSavedTranscript } from '../core/session-sync/saved-transcript.js';
|
|
45
|
+
import { useSessionLogSync } from './use-session-log.js';
|
|
45
46
|
import { useSessionSync } from './use-session-sync.js';
|
|
46
47
|
import { selectTranscriptShapeKey } from './session-transcript-subscription.js';
|
|
47
48
|
import { useSessionStartGiveUp } from './use-session-start-give-up.js';
|
|
48
|
-
import { useSessionTurnOutcome, useSessionWorking } from './use-session-working.js';
|
|
49
|
+
import { useSessionCanStop, useSessionTurnOutcome, useSessionWorking } from './use-session-working.js';
|
|
49
50
|
import { cancelSessionTurn } from './session-stop.js';
|
|
51
|
+
import { useTurnEndHandoff } from './turn-end-handoff.js';
|
|
50
52
|
import { useVisibleAgents } from './use-visible-agents.js';
|
|
51
53
|
import { createBeeuni } from '../core/client/beeuni.js';
|
|
52
54
|
import { platformConfig } from '../core/http/config.js';
|
|
@@ -59,6 +61,9 @@ export function shouldRetrySessionStart(failureCount, error, sessionId) {
|
|
|
59
61
|
}
|
|
60
62
|
return !isSessionStartError(error) && failureCount < 3;
|
|
61
63
|
}
|
|
64
|
+
export function presenceReturnReopensSession(heldByThisTab, cachedStage) {
|
|
65
|
+
return heldByThisTab || cachedStage === 'stopped';
|
|
66
|
+
}
|
|
62
67
|
export const SESSION_START_POLL_MS = 1_500;
|
|
63
68
|
export function resolveSessionRuntimeUrl(start) {
|
|
64
69
|
if (!start || start.stage !== 'ready')
|
|
@@ -407,22 +412,53 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
407
412
|
const presenceAlertsRef = useRef(presenceAlerts);
|
|
408
413
|
const presencePageExitRef = useRef(presencePageExit);
|
|
409
414
|
const presenceReporterRef = useRef(null);
|
|
415
|
+
const lastPresenceActiveRef = useRef(null);
|
|
416
|
+
const [presenceAwake, setPresenceAwakeState] = useState(null);
|
|
417
|
+
const presenceAwakeRef = useRef(presenceAwake);
|
|
418
|
+
const setPresenceAwake = useCallback((next) => {
|
|
419
|
+
if (next === null && presenceAwakeRef.current === null)
|
|
420
|
+
return;
|
|
421
|
+
presenceAwakeRef.current = next;
|
|
422
|
+
setPresenceAwakeState(next);
|
|
423
|
+
}, []);
|
|
424
|
+
const presenceReportRef = useRef(0);
|
|
410
425
|
useEffect(() => {
|
|
411
426
|
if (!browserPresence || !presenceTabId || !projectId || !sessionId)
|
|
412
427
|
return;
|
|
413
428
|
const tab_id = presenceTabId;
|
|
414
429
|
const handle = createBeeuni(platformConfig()).session(projectId, sessionId);
|
|
415
430
|
const reporter = presenceReporter(({ active, alerts }) => {
|
|
431
|
+
if (active &&
|
|
432
|
+
lastPresenceActiveRef.current === false &&
|
|
433
|
+
presenceReturnReopensSession(stoppedByUserRef.current === sessionId, queryClient.getQueryData(sessionStartKey(projectId, sessionId))
|
|
434
|
+
?.stage)) {
|
|
435
|
+
stoppedByUserRef.current = null;
|
|
436
|
+
openedAtRef.current = { sessionId, at: Date.now() };
|
|
437
|
+
void queryClient.invalidateQueries({ queryKey: sessionStartKey(projectId, sessionId) });
|
|
438
|
+
}
|
|
439
|
+
lastPresenceActiveRef.current = active;
|
|
440
|
+
const report = ++presenceReportRef.current;
|
|
441
|
+
if (!active)
|
|
442
|
+
setPresenceAwake(null);
|
|
416
443
|
const keepalive = !active && presencePageExitRef.current;
|
|
417
|
-
void handle
|
|
444
|
+
void handle
|
|
445
|
+
.presence({ tab_id, active, alerts }, { keepalive })
|
|
446
|
+
.then((res) => {
|
|
447
|
+
if (active && report === presenceReportRef.current && res.data) {
|
|
448
|
+
setPresenceAwake(presenceAwakeFrom(res.data));
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
.catch(() => { });
|
|
418
452
|
}, presenceAlertsRef.current);
|
|
419
453
|
presenceReporterRef.current = reporter;
|
|
420
454
|
const stop = watchHumanPresence({ doc: document, win: window }, reporter.report, () => sessionStreamConnected(projectId, sessionId), () => presencePageExitRef.current);
|
|
455
|
+
const refresh = window.setInterval(reporter.refresh, PRESENCE_AWAKE_REFRESH_MS);
|
|
421
456
|
return () => {
|
|
457
|
+
window.clearInterval(refresh);
|
|
422
458
|
presenceReporterRef.current = null;
|
|
423
459
|
stop();
|
|
424
460
|
};
|
|
425
|
-
}, [browserPresence, presenceTabId, projectId, sessionId]);
|
|
461
|
+
}, [browserPresence, presenceTabId, projectId, sessionId, queryClient]);
|
|
426
462
|
useEffect(() => {
|
|
427
463
|
presenceAlertsRef.current = presenceAlerts;
|
|
428
464
|
presenceReporterRef.current?.setAlerts(presenceAlerts);
|
|
@@ -485,6 +521,17 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
485
521
|
const sandbox = startData?.sandbox ?? null;
|
|
486
522
|
const startReady = stage === 'ready';
|
|
487
523
|
const terminal = stage === 'failed' || stage === 'stopped';
|
|
524
|
+
const startReadyRef = useRef(startReady);
|
|
525
|
+
useEffect(() => {
|
|
526
|
+
if (startReady && !startReadyRef.current) {
|
|
527
|
+
presenceReporterRef.current?.refresh();
|
|
528
|
+
}
|
|
529
|
+
else if (!startReady && startReadyRef.current) {
|
|
530
|
+
presenceReportRef.current += 1;
|
|
531
|
+
setPresenceAwake(null);
|
|
532
|
+
}
|
|
533
|
+
startReadyRef.current = startReady;
|
|
534
|
+
}, [startReady]);
|
|
488
535
|
useIsomorphicLayoutEffect(() => {
|
|
489
536
|
if (!startEnabled)
|
|
490
537
|
return;
|
|
@@ -549,6 +596,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
549
596
|
}, [startEnabled, switchedBoxId, refetchStart]);
|
|
550
597
|
useRuntimeEventStream({ enabled: switched, projectId, sessionId });
|
|
551
598
|
const transcriptHistoryEnabled = startEnabled && chatEngine;
|
|
599
|
+
useSessionLogSync(projectId, sessionId, { enabled: startEnabled });
|
|
552
600
|
const transcriptHistory = useSessionTranscriptHistory(projectId, sessionId, transcriptHistoryEnabled);
|
|
553
601
|
const canonicalSession = useCanonicalRuntimeSession({
|
|
554
602
|
projectId,
|
|
@@ -567,11 +615,24 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
567
615
|
enabled,
|
|
568
616
|
runtimeSessionId: ocSessionId,
|
|
569
617
|
});
|
|
618
|
+
const canStop = useSessionCanStop(projectId, sessionId);
|
|
619
|
+
const turnRunning = working.state === 'working';
|
|
620
|
+
const turnRanRef = useRef(turnRunning);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
if (turnRunning) {
|
|
623
|
+
presenceReportRef.current += 1;
|
|
624
|
+
setPresenceAwake(null);
|
|
625
|
+
}
|
|
626
|
+
else if (turnRanRef.current) {
|
|
627
|
+
presenceReporterRef.current?.refresh();
|
|
628
|
+
}
|
|
629
|
+
turnRanRef.current = turnRunning;
|
|
630
|
+
}, [turnRunning]);
|
|
570
631
|
const { noteSendReceipt, acceptSendReceipt, clearSendReceipt } = useSessionWorkingStore.getState();
|
|
571
632
|
const rawSync = useSessionSync(chatEngine ? ocSessionId : '', {
|
|
572
633
|
beeuniSessionScope: `${projectId}/${sessionId}`,
|
|
573
634
|
streamConnected,
|
|
574
|
-
mirror: transcriptHistory.envelope
|
|
635
|
+
mirror: transcriptHistory.envelope,
|
|
575
636
|
networkEnabled: switched,
|
|
576
637
|
working: working.state === 'working',
|
|
577
638
|
serverHoldsTurn: working.serverOpenTurnToken !== null,
|
|
@@ -589,6 +650,25 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
589
650
|
const messages = useMemo(() => messagesBeforeRewind(sync.messages, restRewind), [sync.messages, restRewind]);
|
|
590
651
|
const turnOutcome = useSessionTurnOutcome(projectId, sessionId);
|
|
591
652
|
const turnRead = typeof turnOutcome.atMs === 'number';
|
|
653
|
+
const lastEndedToken = turnOutcome.last_ended?.turn_token ?? null;
|
|
654
|
+
const lastEndedSeenRef = useRef(undefined);
|
|
655
|
+
useEffect(() => {
|
|
656
|
+
if (!turnRead)
|
|
657
|
+
return;
|
|
658
|
+
const seen = lastEndedSeenRef.current;
|
|
659
|
+
lastEndedSeenRef.current = lastEndedToken;
|
|
660
|
+
if (seen !== undefined && seen !== lastEndedToken)
|
|
661
|
+
presenceReporterRef.current?.refresh();
|
|
662
|
+
}, [turnRead, lastEndedToken]);
|
|
663
|
+
useTurnEndHandoff({
|
|
664
|
+
enabled,
|
|
665
|
+
projectId,
|
|
666
|
+
sessionId,
|
|
667
|
+
runtimeSessionId: ocSessionId || null,
|
|
668
|
+
working: turnRunning,
|
|
669
|
+
openTurnToken: working.serverOpenTurnToken,
|
|
670
|
+
lastEndedToken,
|
|
671
|
+
});
|
|
592
672
|
const savedEmptyRoot = transcriptHistoryEnabled ? transcriptHistory.emptyRootSessionId : null;
|
|
593
673
|
const emptyProvenForRoot = savedEmptyRoot !== null && savedEmptyRoot === ocSessionId;
|
|
594
674
|
const savedTranscript = resolveSavedTranscript({
|
|
@@ -855,6 +935,8 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
855
935
|
runtimePhase,
|
|
856
936
|
working,
|
|
857
937
|
isBusy: working.state === 'working',
|
|
938
|
+
canStop,
|
|
939
|
+
presenceAwake,
|
|
858
940
|
isCompacting,
|
|
859
941
|
isLoading: sync.isLoading,
|
|
860
942
|
savedTranscript,
|