@kortix/sdk 0.13.4 → 0.13.7
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 +63 -6
- package/dist/browser/cache/idb-sync-cache.d.ts +19 -1
- package/dist/browser/cache/idb-sync-cache.d.ts.map +1 -1
- package/dist/browser/cache/idb-sync-cache.js +39 -4
- package/dist/browser/cache/idb-sync-cache.js.map +1 -1
- package/dist/browser/session-sync/server-transcript-mirror.d.ts +82 -0
- package/dist/browser/session-sync/server-transcript-mirror.d.ts.map +1 -0
- package/dist/browser/session-sync/server-transcript-mirror.js +143 -0
- package/dist/browser/session-sync/server-transcript-mirror.js.map +1 -0
- package/dist/browser/session-sync/session-sync-registry.d.ts +11 -1
- package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
- package/dist/browser/session-sync/session-sync-registry.js +117 -9
- package/dist/browser/session-sync/session-sync-registry.js.map +1 -1
- package/dist/browser/session-sync/visibility.d.ts +19 -0
- package/dist/browser/session-sync/visibility.d.ts.map +1 -0
- package/dist/browser/session-sync/visibility.js +16 -0
- package/dist/browser/session-sync/visibility.js.map +1 -0
- package/dist/browser/stores/sandbox-connection-store.d.ts +13 -1
- package/dist/browser/stores/sandbox-connection-store.d.ts.map +1 -1
- package/dist/browser/stores/sandbox-connection-store.js +6 -3
- package/dist/browser/stores/sandbox-connection-store.js.map +1 -1
- package/dist/browser/stores/session-working-store.d.ts +10 -5
- package/dist/browser/stores/session-working-store.d.ts.map +1 -1
- package/dist/browser/stores/session-working-store.js +32 -8
- package/dist/browser/stores/session-working-store.js.map +1 -1
- package/dist/browser/stores/sync-store.d.ts +56 -2
- package/dist/browser/stores/sync-store.d.ts.map +1 -1
- package/dist/browser/stores/sync-store.js +682 -402
- package/dist/browser/stores/sync-store.js.map +1 -1
- package/dist/core/auth/app-viewer.d.ts +57 -0
- package/dist/core/auth/app-viewer.d.ts.map +1 -0
- package/dist/core/auth/app-viewer.js +82 -0
- package/dist/core/auth/app-viewer.js.map +1 -0
- package/dist/core/auth/session.d.ts +45 -0
- package/dist/core/auth/session.d.ts.map +1 -0
- package/dist/core/auth/session.js +113 -0
- package/dist/core/auth/session.js.map +1 -0
- package/dist/core/client/kortix.d.ts +37 -29
- package/dist/core/client/kortix.d.ts.map +1 -1
- package/dist/core/client/kortix.js +61 -8
- package/dist/core/client/kortix.js.map +1 -1
- package/dist/core/http/config.d.ts +4 -2
- package/dist/core/http/config.d.ts.map +1 -1
- package/dist/core/http/config.js.map +1 -1
- package/dist/core/http/opencode-errors.d.ts +17 -0
- package/dist/core/http/opencode-errors.d.ts.map +1 -1
- package/dist/core/http/opencode-errors.js +25 -0
- package/dist/core/http/opencode-errors.js.map +1 -1
- package/dist/core/rest/platform-client/auth.d.ts +120 -0
- package/dist/core/rest/platform-client/auth.d.ts.map +1 -0
- package/dist/core/rest/platform-client/auth.js +98 -0
- package/dist/core/rest/platform-client/auth.js.map +1 -0
- package/dist/core/rest/platform-client/host-boundary.d.ts +5 -0
- package/dist/core/rest/platform-client/host-boundary.d.ts.map +1 -1
- package/dist/core/rest/platform-client/host-boundary.js.map +1 -1
- package/dist/core/rest/projects-client/accounts.d.ts +57 -0
- package/dist/core/rest/projects-client/accounts.d.ts.map +1 -1
- package/dist/core/rest/projects-client/accounts.js +31 -0
- package/dist/core/rest/projects-client/accounts.js.map +1 -1
- package/dist/core/rest/projects-client/apps.d.ts +9 -0
- package/dist/core/rest/projects-client/apps.d.ts.map +1 -1
- package/dist/core/rest/projects-client/apps.js.map +1 -1
- package/dist/core/rest/projects-client/billing.d.ts +2 -0
- package/dist/core/rest/projects-client/billing.d.ts.map +1 -1
- package/dist/core/rest/projects-client/billing.js.map +1 -1
- package/dist/core/rest/projects-client/channels.d.ts +0 -3
- package/dist/core/rest/projects-client/channels.d.ts.map +1 -1
- package/dist/core/rest/projects-client/channels.js +0 -6
- package/dist/core/rest/projects-client/channels.js.map +1 -1
- package/dist/core/rest/projects-client/connectors.d.ts +104 -19
- package/dist/core/rest/projects-client/connectors.d.ts.map +1 -1
- package/dist/core/rest/projects-client/connectors.js +56 -2
- package/dist/core/rest/projects-client/connectors.js.map +1 -1
- package/dist/core/rest/projects-client/index.d.ts +1 -2
- package/dist/core/rest/projects-client/index.d.ts.map +1 -1
- package/dist/core/rest/projects-client/index.js +1 -2
- package/dist/core/rest/projects-client/index.js.map +1 -1
- package/dist/core/rest/projects-client/oauth-clients.d.ts +47 -0
- package/dist/core/rest/projects-client/oauth-clients.d.ts.map +1 -0
- package/dist/core/rest/projects-client/oauth-clients.js +33 -0
- package/dist/core/rest/projects-client/oauth-clients.js.map +1 -0
- package/dist/core/rest/projects-client/projects.d.ts +23 -6
- package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
- package/dist/core/rest/projects-client/projects.js +6 -3
- package/dist/core/rest/projects-client/projects.js.map +1 -1
- package/dist/core/rest/projects-client/session-sandbox.d.ts +52 -0
- package/dist/core/rest/projects-client/session-sandbox.d.ts.map +1 -1
- package/dist/core/rest/projects-client/session-sandbox.js.map +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +142 -34
- package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
- package/dist/core/rest/projects-client/sessions.js +48 -19
- package/dist/core/rest/projects-client/sessions.js.map +1 -1
- package/dist/core/runtime/attachment-part.d.ts +26 -0
- package/dist/core/runtime/attachment-part.d.ts.map +1 -0
- package/dist/core/runtime/attachment-part.js +41 -0
- package/dist/core/runtime/attachment-part.js.map +1 -0
- package/dist/core/runtime/client.d.ts +4 -2
- package/dist/core/runtime/client.d.ts.map +1 -1
- package/dist/core/runtime/client.js +3 -2
- package/dist/core/runtime/client.js.map +1 -1
- package/dist/core/runtime/opencode-v2-event-aliases.d.ts +184 -0
- package/dist/core/runtime/opencode-v2-event-aliases.d.ts.map +1 -0
- package/dist/core/runtime/opencode-v2-event-aliases.js +2 -0
- package/dist/core/runtime/opencode-v2-event-aliases.js.map +1 -0
- package/dist/core/runtime/pty.d.ts +11 -1
- package/dist/core/runtime/pty.d.ts.map +1 -1
- package/dist/core/runtime/pty.js +19 -6
- package/dist/core/runtime/pty.js.map +1 -1
- package/dist/core/session/connection.d.ts +64 -0
- package/dist/core/session/connection.d.ts.map +1 -0
- package/dist/core/session/connection.js +36 -0
- package/dist/core/session/connection.js.map +1 -0
- package/dist/core/session/index.d.ts +2 -0
- package/dist/core/session/index.d.ts.map +1 -1
- package/dist/core/session/index.js +2 -0
- package/dist/core/session/index.js.map +1 -1
- package/dist/core/session/open-bundle.d.ts +110 -0
- package/dist/core/session/open-bundle.d.ts.map +1 -0
- package/dist/core/session/open-bundle.js +183 -0
- package/dist/core/session/open-bundle.js.map +1 -0
- package/dist/core/session/poll-owner.d.ts +35 -0
- package/dist/core/session/poll-owner.d.ts.map +1 -0
- package/dist/core/session/poll-owner.js +90 -0
- package/dist/core/session/poll-owner.js.map +1 -0
- package/dist/core/session/preview-auth.d.ts +25 -0
- package/dist/core/session/preview-auth.d.ts.map +1 -1
- package/dist/core/session/preview-auth.js +31 -1
- package/dist/core/session/preview-auth.js.map +1 -1
- package/dist/core/session/preview-config.d.ts +26 -0
- package/dist/core/session/preview-config.d.ts.map +1 -0
- package/dist/core/session/preview-config.js +95 -0
- package/dist/core/session/preview-config.js.map +1 -0
- package/dist/core/session/preview-options.d.ts +25 -0
- package/dist/core/session/preview-options.d.ts.map +1 -0
- package/dist/core/session/preview-options.js +45 -0
- package/dist/core/session/preview-options.js.map +1 -0
- package/dist/core/session/preview.d.ts +8 -3
- package/dist/core/session/preview.d.ts.map +1 -1
- package/dist/core/session/preview.js +11 -4
- package/dist/core/session/preview.js.map +1 -1
- package/dist/core/session/rewind.d.ts +67 -24
- package/dist/core/session/rewind.d.ts.map +1 -1
- package/dist/core/session/rewind.js +74 -26
- package/dist/core/session/rewind.js.map +1 -1
- package/dist/core/session/server-store/active.d.ts +10 -12
- package/dist/core/session/server-store/active.d.ts.map +1 -1
- package/dist/core/session/server-store/active.js +11 -9
- package/dist/core/session/server-store/active.js.map +1 -1
- package/dist/core/session/server-store/url-helpers.d.ts +11 -2
- package/dist/core/session/server-store/url-helpers.d.ts.map +1 -1
- package/dist/core/session/server-store/url-helpers.js +11 -2
- package/dist/core/session/server-store/url-helpers.js.map +1 -1
- package/dist/core/session/static-file-preview.d.ts +50 -0
- package/dist/core/session/static-file-preview.d.ts.map +1 -0
- package/dist/core/session/static-file-preview.js +91 -0
- package/dist/core/session/static-file-preview.js.map +1 -0
- package/dist/core/session/url.d.ts +25 -2
- package/dist/core/session/url.d.ts.map +1 -1
- package/dist/core/session/url.js +87 -11
- package/dist/core/session/url.js.map +1 -1
- package/dist/core/session/wake-escalation.d.ts +188 -0
- package/dist/core/session/wake-escalation.d.ts.map +1 -0
- package/dist/core/session/wake-escalation.js +241 -0
- package/dist/core/session/wake-escalation.js.map +1 -0
- package/dist/core/session/working.d.ts +107 -2
- package/dist/core/session/working.d.ts.map +1 -1
- package/dist/core/session/working.js +206 -7
- package/dist/core/session/working.js.map +1 -1
- package/dist/core/session-sync/durable-log.d.ts +96 -0
- package/dist/core/session-sync/durable-log.d.ts.map +1 -0
- package/dist/core/session-sync/durable-log.js +94 -0
- package/dist/core/session-sync/durable-log.js.map +1 -0
- package/dist/core/session-sync/fragment.d.ts +27 -0
- package/dist/core/session-sync/fragment.d.ts.map +1 -0
- package/dist/core/session-sync/fragment.js +24 -0
- package/dist/core/session-sync/fragment.js.map +1 -0
- package/dist/core/session-sync/session-sync-controller.d.ts +117 -11
- package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
- package/dist/core/session-sync/session-sync-controller.js +278 -26
- package/dist/core/session-sync/session-sync-controller.js.map +1 -1
- package/dist/core/stream/keepalive.d.ts +17 -0
- package/dist/core/stream/keepalive.d.ts.map +1 -0
- package/dist/core/stream/keepalive.js +19 -0
- package/dist/core/stream/keepalive.js.map +1 -0
- package/dist/core/turns/grouping.d.ts.map +1 -1
- package/dist/core/turns/grouping.js +73 -23
- package/dist/core/turns/grouping.js.map +1 -1
- package/dist/core/turns/types.d.ts +5 -0
- package/dist/core/turns/types.d.ts.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/kortix.esm.min.js +25 -25
- package/dist/kortix.global.js +25 -25
- package/dist/node/app-viewer.d.ts +41 -0
- package/dist/node/app-viewer.d.ts.map +1 -0
- package/dist/node/app-viewer.js +150 -0
- package/dist/node/app-viewer.js.map +1 -0
- package/dist/node/auth.d.ts +84 -0
- package/dist/node/auth.d.ts.map +1 -0
- package/dist/node/auth.js +414 -0
- package/dist/node/auth.js.map +1 -0
- package/dist/node/server.d.ts +19 -0
- package/dist/node/server.d.ts.map +1 -1
- package/dist/node/server.js +13 -1
- package/dist/node/server.js.map +1 -1
- package/dist/platform/auth-core.d.ts +13 -1
- package/dist/platform/auth-core.d.ts.map +1 -1
- package/dist/platform/auth-core.js +13 -1
- package/dist/platform/auth-core.js.map +1 -1
- package/dist/react/bedrock-invokable.d.ts +58 -0
- package/dist/react/bedrock-invokable.d.ts.map +1 -0
- package/dist/react/bedrock-invokable.js +83 -0
- package/dist/react/bedrock-invokable.js.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +7 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/model-flatten.d.ts +14 -1
- package/dist/react/model-flatten.d.ts.map +1 -1
- package/dist/react/model-flatten.js +3 -4
- package/dist/react/model-flatten.js.map +1 -1
- package/dist/react/opencode.d.ts +3 -1
- package/dist/react/opencode.d.ts.map +1 -1
- package/dist/react/opencode.js +5 -1
- package/dist/react/opencode.js.map +1 -1
- package/dist/react/provider-selection.d.ts +58 -0
- package/dist/react/provider-selection.d.ts.map +1 -1
- package/dist/react/provider-selection.js +253 -2
- package/dist/react/provider-selection.js.map +1 -1
- package/dist/react/session-start-giveup.d.ts +12 -0
- package/dist/react/session-start-giveup.d.ts.map +1 -0
- package/dist/react/session-start-giveup.js +13 -0
- package/dist/react/session-start-giveup.js.map +1 -0
- package/dist/react/session-title-sync.d.ts.map +1 -1
- package/dist/react/session-title-sync.js +15 -5
- package/dist/react/session-title-sync.js.map +1 -1
- package/dist/react/sign-in-with-kortix.d.ts +346 -0
- package/dist/react/sign-in-with-kortix.d.ts.map +1 -0
- package/dist/react/sign-in-with-kortix.js +65 -0
- package/dist/react/sign-in-with-kortix.js.map +1 -0
- package/dist/react/use-admin-accounts.d.ts +36 -1
- package/dist/react/use-admin-accounts.d.ts.map +1 -1
- package/dist/react/use-admin-accounts.js +19 -0
- package/dist/react/use-admin-accounts.js.map +1 -1
- package/dist/react/use-gateway-routing-policy.d.ts +1 -0
- package/dist/react/use-gateway-routing-policy.d.ts.map +1 -1
- package/dist/react/use-gateway-routing-policy.js +12 -1
- package/dist/react/use-gateway-routing-policy.js.map +1 -1
- package/dist/react/use-kortix-app-viewer.d.ts +13 -0
- package/dist/react/use-kortix-app-viewer.d.ts.map +1 -0
- package/dist/react/use-kortix-app-viewer.js +29 -0
- package/dist/react/use-kortix-app-viewer.js.map +1 -0
- package/dist/react/use-model-defaults.d.ts +7 -0
- package/dist/react/use-model-defaults.d.ts.map +1 -1
- package/dist/react/use-model-defaults.js +7 -1
- package/dist/react/use-model-defaults.js.map +1 -1
- package/dist/react/use-model-enablement.d.ts.map +1 -1
- package/dist/react/use-model-enablement.js +5 -1
- package/dist/react/use-model-enablement.js.map +1 -1
- package/dist/react/use-model-store.d.ts +31 -0
- package/dist/react/use-model-store.d.ts.map +1 -1
- package/dist/react/use-model-store.js +31 -2
- package/dist/react/use-model-store.js.map +1 -1
- package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
- package/dist/react/use-opencode-events/handle-event.js +26 -0
- package/dist/react/use-opencode-events/handle-event.js.map +1 -1
- package/dist/react/use-opencode-events/helpers.d.ts +49 -5
- package/dist/react/use-opencode-events/helpers.d.ts.map +1 -1
- package/dist/react/use-opencode-events/helpers.js +54 -6
- package/dist/react/use-opencode-events/helpers.js.map +1 -1
- package/dist/react/use-opencode-events/index.d.ts.map +1 -1
- package/dist/react/use-opencode-events/index.js +80 -8
- package/dist/react/use-opencode-events/index.js.map +1 -1
- package/dist/react/use-opencode-events/rehydrate-targets.d.ts +18 -0
- package/dist/react/use-opencode-events/rehydrate-targets.d.ts.map +1 -0
- package/dist/react/use-opencode-events/rehydrate-targets.js +20 -0
- package/dist/react/use-opencode-events/rehydrate-targets.js.map +1 -0
- package/dist/react/use-opencode-events/stream-revival.d.ts +37 -0
- package/dist/react/use-opencode-events/stream-revival.d.ts.map +1 -0
- package/dist/react/use-opencode-events/stream-revival.js +53 -0
- package/dist/react/use-opencode-events/stream-revival.js.map +1 -0
- package/dist/react/use-opencode-events/use-event-stream-refs.d.ts.map +1 -1
- package/dist/react/use-opencode-events/use-event-stream-refs.js +14 -2
- package/dist/react/use-opencode-events/use-event-stream-refs.js.map +1 -1
- package/dist/react/use-opencode-local.d.ts.map +1 -1
- package/dist/react/use-opencode-local.js +27 -8
- package/dist/react/use-opencode-local.js.map +1 -1
- package/dist/react/use-opencode-pty.d.ts +7 -1
- package/dist/react/use-opencode-pty.d.ts.map +1 -1
- package/dist/react/use-opencode-pty.js +6 -2
- package/dist/react/use-opencode-pty.js.map +1 -1
- package/dist/react/use-opencode-sessions/agents.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/agents.js +13 -2
- package/dist/react/use-opencode-sessions/agents.js.map +1 -1
- package/dist/react/use-opencode-sessions/commands.d.ts +12 -0
- package/dist/react/use-opencode-sessions/commands.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/commands.js +15 -3
- package/dist/react/use-opencode-sessions/commands.js.map +1 -1
- package/dist/react/use-opencode-sessions/index.d.ts +1 -0
- package/dist/react/use-opencode-sessions/index.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/index.js +1 -0
- package/dist/react/use-opencode-sessions/index.js.map +1 -1
- package/dist/react/use-opencode-sessions/keys.d.ts +8 -0
- package/dist/react/use-opencode-sessions/keys.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/keys.js +8 -0
- package/dist/react/use-opencode-sessions/keys.js.map +1 -1
- package/dist/react/use-opencode-sessions/providers.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/providers.js +56 -5
- package/dist/react/use-opencode-sessions/providers.js.map +1 -1
- package/dist/react/use-opencode-sessions/shared.d.ts +23 -0
- package/dist/react/use-opencode-sessions/shared.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/shared.js +31 -0
- package/dist/react/use-opencode-sessions/shared.js.map +1 -1
- package/dist/react/use-opencode-sessions/vcs.d.ts +28 -0
- package/dist/react/use-opencode-sessions/vcs.d.ts.map +1 -0
- package/dist/react/use-opencode-sessions/vcs.js +42 -0
- package/dist/react/use-opencode-sessions/vcs.js.map +1 -0
- package/dist/react/use-poll-owner.d.ts +14 -0
- package/dist/react/use-poll-owner.d.ts.map +1 -0
- package/dist/react/use-poll-owner.js +32 -0
- package/dist/react/use-poll-owner.js.map +1 -0
- package/dist/react/use-project-llm-gateway.d.ts +27 -0
- package/dist/react/use-project-llm-gateway.d.ts.map +1 -0
- package/dist/react/use-project-llm-gateway.js +41 -0
- package/dist/react/use-project-llm-gateway.js.map +1 -0
- package/dist/react/use-project-models.d.ts +9 -0
- package/dist/react/use-project-models.d.ts.map +1 -1
- package/dist/react/use-project-models.js +24 -1
- package/dist/react/use-project-models.js.map +1 -1
- package/dist/react/use-runtime-reconnect.d.ts +11 -0
- package/dist/react/use-runtime-reconnect.d.ts.map +1 -1
- package/dist/react/use-runtime-reconnect.js +34 -3
- package/dist/react/use-runtime-reconnect.js.map +1 -1
- package/dist/react/use-session-prompts.d.ts +53 -3
- package/dist/react/use-session-prompts.d.ts.map +1 -1
- package/dist/react/use-session-prompts.js +133 -21
- package/dist/react/use-session-prompts.js.map +1 -1
- package/dist/react/use-session-send.d.ts +17 -0
- package/dist/react/use-session-send.d.ts.map +1 -1
- package/dist/react/use-session-send.js +54 -5
- package/dist/react/use-session-send.js.map +1 -1
- package/dist/react/use-session-start-give-up.d.ts +20 -0
- package/dist/react/use-session-start-give-up.d.ts.map +1 -0
- package/dist/react/use-session-start-give-up.js +60 -0
- package/dist/react/use-session-start-give-up.js.map +1 -0
- package/dist/react/use-session-sync.d.ts +35 -1
- package/dist/react/use-session-sync.d.ts.map +1 -1
- package/dist/react/use-session-sync.js +135 -53
- package/dist/react/use-session-sync.js.map +1 -1
- package/dist/react/use-session-working.d.ts +55 -0
- package/dist/react/use-session-working.d.ts.map +1 -1
- package/dist/react/use-session-working.js +114 -13
- package/dist/react/use-session-working.js.map +1 -1
- package/dist/react/use-session.d.ts +13 -13
- package/dist/react/use-session.d.ts.map +1 -1
- package/dist/react/use-session.js +74 -53
- package/dist/react/use-session.js.map +1 -1
- package/dist/react/use-static-file-preview.d.ts +12 -0
- package/dist/react/use-static-file-preview.d.ts.map +1 -0
- package/dist/react/use-static-file-preview.js +119 -0
- package/dist/react/use-static-file-preview.js.map +1 -0
- package/dist/react/use-wake-escalation.d.ts +51 -0
- package/dist/react/use-wake-escalation.d.ts.map +1 -0
- package/dist/react/use-wake-escalation.js +62 -0
- package/dist/react/use-wake-escalation.js.map +1 -0
- package/package.json +6 -4
- package/dist/browser/session-sync/session-transcript-cache.d.ts +0 -79
- package/dist/browser/session-sync/session-transcript-cache.d.ts.map +0 -1
- package/dist/browser/session-sync/session-transcript-cache.js +0 -91
- package/dist/browser/session-sync/session-transcript-cache.js.map +0 -1
- package/dist/core/rest/projects-client/public-voice-join.d.ts +0 -59
- package/dist/core/rest/projects-client/public-voice-join.d.ts.map +0 -1
- package/dist/core/rest/projects-client/public-voice-join.js +0 -64
- package/dist/core/rest/projects-client/public-voice-join.js.map +0 -1
- package/dist/core/rest/projects-client/transcription.d.ts +0 -5
- package/dist/core/rest/projects-client/transcription.d.ts.map +0 -1
- package/dist/core/rest/projects-client/transcription.js +0 -14
- package/dist/core/rest/projects-client/transcription.js.map +0 -1
|
@@ -45,6 +45,35 @@ export const STREAM_OBSERVATION_MAX_MS = 45_000;
|
|
|
45
45
|
* Stop with its reply on screen for up to 45s.
|
|
46
46
|
*/
|
|
47
47
|
export const INBOX_OBSERVATION_MAX_MS = 10_000;
|
|
48
|
+
/**
|
|
49
|
+
* How long a runtime `idle` frame may outrank the control plane's turn row.
|
|
50
|
+
*
|
|
51
|
+
* The ledger is closed by a SEPARATE daemon relay (`POST .../turn-stream`
|
|
52
|
+
* `kind:"end"`), so for a moment after the runtime goes idle the row is still
|
|
53
|
+
* open and a `/turn` read taken in that window honestly reports a turn that has
|
|
54
|
+
* finished. MEASURED 2026-08-21 on a healthy stack: the relay POST itself took
|
|
55
|
+
* 1765ms, while the idle frame reaches the tab immediately and triggers its own
|
|
56
|
+
* refetch that lands ~186ms later — squarely inside the gap. Without this the
|
|
57
|
+
* composer's Stop button and the turn shimmer came back ~200ms after the reply
|
|
58
|
+
* and stayed for the whole poll interval.
|
|
59
|
+
*
|
|
60
|
+
* BOUNDED, and this is the important half. An idle frame is NOT proof the turn
|
|
61
|
+
* ended: `session.error` is relayed as `idle` too ("errors terminate the
|
|
62
|
+
* response"), and OpenCode emits it while it is RETRYING a provider — a 429
|
|
63
|
+
* backoff, a transient upstream 5xx — with the turn still very much live. An
|
|
64
|
+
* unbounded rule therefore un-busied the composer mid-turn on every retry and
|
|
65
|
+
* held it that way until the next `busy` frame, which is a worse flicker than
|
|
66
|
+
* the one it fixes. Past this window the ledger is believed again, so a retry
|
|
67
|
+
* blip self-heals in seconds instead of lasting the stream's whole 45s life.
|
|
68
|
+
*
|
|
69
|
+
* Comfortably above the measured relay, far below a provider backoff.
|
|
70
|
+
*/
|
|
71
|
+
export const TURN_END_LEDGER_LAG_MS = 3_000;
|
|
72
|
+
// RETAINED AS A MEASUREMENT, NOT A RULE. It no longer gates anything: the veto
|
|
73
|
+
// above is causal now, because a row still open past this lag is far more often
|
|
74
|
+
// a dropped `kind:"end"` relay (turn over) than a live retry (turn running), and
|
|
75
|
+
// guessing wrong in the first case is a visible oscillation. Exported because
|
|
76
|
+
// this package's names are a public contract.
|
|
48
77
|
/**
|
|
49
78
|
* How long a stop this tab issued may bar a server read from reporting the
|
|
50
79
|
* turn it is ending.
|
|
@@ -56,6 +85,25 @@ export const INBOX_OBSERVATION_MAX_MS = 10_000;
|
|
|
56
85
|
* because the evidence arrives sooner.
|
|
57
86
|
*/
|
|
58
87
|
export const OPTIMISTIC_ABORT_MAX_MS = 15_000;
|
|
88
|
+
/**
|
|
89
|
+
* May `candidate` replace `current` as the session's inbox observation?
|
|
90
|
+
*
|
|
91
|
+
* ONE clock per comparison. When both observations carry the server's stamp,
|
|
92
|
+
* the server clock decides — it is the only pair drawn from one clock, and it
|
|
93
|
+
* orders a read against a write exactly (a read issued before a POST carries an
|
|
94
|
+
* older `observed_at` than the POST's response, so it can never erase it).
|
|
95
|
+
* Otherwise the browser stamps decide, as before. Ties stand aside for the
|
|
96
|
+
* candidate: two reads of the same instant agree, and the newer arrival may
|
|
97
|
+
* carry a corrected list.
|
|
98
|
+
*/
|
|
99
|
+
export function inboxObservationSupersedes(candidate, current) {
|
|
100
|
+
if (!current)
|
|
101
|
+
return true;
|
|
102
|
+
if (current.serverAtMs != null && candidate.serverAtMs != null) {
|
|
103
|
+
return candidate.serverAtMs >= current.serverAtMs;
|
|
104
|
+
}
|
|
105
|
+
return candidate.atMs >= current.atMs;
|
|
106
|
+
}
|
|
59
107
|
/**
|
|
60
108
|
* How many inbox rows are work the server still intends to run.
|
|
61
109
|
*
|
|
@@ -119,8 +167,13 @@ function instant(value) {
|
|
|
119
167
|
* must re-evaluate at those instants — see `workingExpiryAtMs`.
|
|
120
168
|
*/
|
|
121
169
|
export function projectWorking(inputs) {
|
|
122
|
-
const { optimistic, abort, inbox, server, stream, nowMs } = inputs;
|
|
170
|
+
const { optimistic, abort, inbox, server, stream, activity, nowMs } = inputs;
|
|
123
171
|
const receiptLive = !!optimistic && nowMs - optimistic.atMs < OPTIMISTIC_RECEIPT_MAX_MS;
|
|
172
|
+
const receiptTurnId = optimistic
|
|
173
|
+
? optimistic.turnId === undefined
|
|
174
|
+
? optimistic.messageId
|
|
175
|
+
: optimistic.turnId
|
|
176
|
+
: null;
|
|
124
177
|
// TWO floors, because the two server-side observers have different knowledge.
|
|
125
178
|
//
|
|
126
179
|
// `GET .../turn` reads the control plane's ledger, and there is NO row in it
|
|
@@ -151,13 +204,143 @@ export function projectWorking(inputs) {
|
|
|
151
204
|
const serverFresh = !!server && nowMs - server.atMs <= SERVER_OBSERVATION_MAX_MS;
|
|
152
205
|
const streamFresh = !!stream && nowMs - stream.atMs <= STREAM_OBSERVATION_MAX_MS;
|
|
153
206
|
const inboxFresh = !!inbox && nowMs - inbox.atMs <= INBOX_OBSERVATION_MAX_MS;
|
|
154
|
-
|
|
155
|
-
|
|
207
|
+
// The runtime's own end-of-turn frame.
|
|
208
|
+
//
|
|
209
|
+
// NOT gated on `streamFresh`, unlike every branch that decides `working` from
|
|
210
|
+
// the stream. The bound exists because an old frame cannot testify to what is
|
|
211
|
+
// happening NOW — but this frame is not asked that. It is asked whether a turn
|
|
212
|
+
// that started BEFORE it has ended, and that answer does not rot: a turn which
|
|
213
|
+
// resumed would have produced a newer, non-idle frame, and then this is not an
|
|
214
|
+
// idle frame at all. Gating it cost exactly what the 3s window cost, 42s later
|
|
215
|
+
// and permanently — at `stream.atMs + STREAM_OBSERVATION_MAX_MS` the veto
|
|
216
|
+
// vanished with no new input, and a row the relay never closed put the
|
|
217
|
+
// composer back on Stop until its deadline (240 MINUTES for an accepted turn).
|
|
218
|
+
// WIRE frames only. A fabricated local idle (`origin: 'local'`) is the tab
|
|
219
|
+
// inferring, not the runtime speaking, and this veto is unbounded — one wrong
|
|
220
|
+
// local frame silenced every fresh `/turn` read for the rest of a quiet turn
|
|
221
|
+
// (dev, 2026-08-24: busy indicator gone, composer on Send, transcript poll
|
|
222
|
+
// switched off, all mid-run). See `WorkingStreamInput.origin`.
|
|
223
|
+
const idleFrame = stream && stream.type === 'idle' && stream.origin !== 'local' ? stream : null;
|
|
224
|
+
// CONTENT FIRST. Bounded by the stream's own freshness rule, because it
|
|
225
|
+
// arrives on the same transport and goes stale for the same reasons — but
|
|
226
|
+
// within that window it outranks every observer, including an idle frame it
|
|
227
|
+
// postdates. A runtime that is emitting parts is working, whatever the last
|
|
228
|
+
// status frame said and whatever a poll that has not answered yet will say.
|
|
229
|
+
const activityFresh = !!activity && nowMs - activity.atMs <= STREAM_OBSERVATION_MAX_MS;
|
|
230
|
+
const activityAfterIdle = activityFresh && (!idleFrame || activity.atMs > idleFrame.atMs);
|
|
231
|
+
/**
|
|
232
|
+
* Whether the runtime has already finished this turn.
|
|
233
|
+
*
|
|
234
|
+
* "Newer read wins" is the rule this replaces, and it is wrong here, because
|
|
235
|
+
* the two observers do not learn the same fact at the same time. The idle
|
|
236
|
+
* frame comes straight off the runtime over SSE. The ledger row is closed by
|
|
237
|
+
* a SEPARATE daemon relay (`POST .../turn-stream` `kind:"end"`) — so a `/turn`
|
|
238
|
+
* read ISSUED after the frame is still ABOUT a turn the frame already ended.
|
|
239
|
+
*
|
|
240
|
+
* MEASURED, local stack 2026-08-21, one ordinary composer turn: the idle
|
|
241
|
+
* frame reached the tab at 00:03:59.964, the refetch that frame itself
|
|
242
|
+
* triggers landed at 00:04:00.150 stamped 44ms later and still reported the
|
|
243
|
+
* turn `active`, and the ledger did not record `ended_at` until 00:04:15.132
|
|
244
|
+
* — the relay for that turn never arrived and a reconciliation sweep closed
|
|
245
|
+
* it 15.1s late. The composer's Stop button and the turn's shimmer came back
|
|
246
|
+
* 186ms after they left and stayed for fifteen seconds, with the finished
|
|
247
|
+
* reply already on screen. Even in the healthy case the relay lands ~200ms
|
|
248
|
+
* after the frame, which is still inside the window its own refetch lands in.
|
|
249
|
+
*
|
|
250
|
+
* `started_at` is what separates the two turns the rule has to tell apart: a
|
|
251
|
+
* turn that began BEFORE the frame is the turn that frame ended, and a turn
|
|
252
|
+
* that began after it is a NEW one the frame knows nothing about — a queued
|
|
253
|
+
* prompt draining, a trigger firing, a second device sending. That one keeps
|
|
254
|
+
* the ledger's full authority, with no delay and no window.
|
|
255
|
+
*
|
|
256
|
+
* A row with no start instant (a legacy `activeTurn`) cannot be ranked
|
|
257
|
+
* against the frame at all, and inventing an order there would hide a live
|
|
258
|
+
* turn. The ledger keeps it.
|
|
259
|
+
*/
|
|
260
|
+
const endedByRuntime = (candidate) => {
|
|
261
|
+
if (!idleFrame)
|
|
262
|
+
return false;
|
|
263
|
+
// Only a turn that began BEFORE the frame — a turn started after it is a
|
|
264
|
+
// new one the frame knows nothing about.
|
|
265
|
+
const startedAt = instant(candidate.started_at);
|
|
266
|
+
if (startedAt === null || startedAt >= idleFrame.atMs)
|
|
267
|
+
return false;
|
|
268
|
+
// And that is the whole rule. It used to expire after
|
|
269
|
+
// `TURN_END_LEDGER_LAG_MS`, on the theory that a row still open past the
|
|
270
|
+
// relay's lag must mean the frame was a retry's `session.error` rather than
|
|
271
|
+
// the end of anything. But time is not evidence: when the `kind:"end"`
|
|
272
|
+
// relay is simply DROPPED — the documented failure mode, closed by a
|
|
273
|
+
// reconciliation sweep 15.1s late in this file's own measurement — the row
|
|
274
|
+
// stays open for exactly the same reason and the turn is exactly as
|
|
275
|
+
// finished. Expiring the veto therefore announced a finished turn again,
|
|
276
|
+
// and the user watched "Gathering thoughts…" and the Stop button come back
|
|
277
|
+
// for seconds with the answer already on screen (dev, 2026-08-23).
|
|
278
|
+
//
|
|
279
|
+
// A turn that is genuinely still running says so, and that is what returns
|
|
280
|
+
// authority to the ledger: any newer frame is not idle, so `idleFrame` is
|
|
281
|
+
// null on the next evaluation and the row decides again — immediately, with
|
|
282
|
+
// no window to tune.
|
|
283
|
+
//
|
|
284
|
+
// "Says so" assumes the stream is delivering. When it is NOT — a
|
|
285
|
+
// black-holed SSE proxy answers 200 and never writes a byte, and the
|
|
286
|
+
// client heartbeat is the only detector (apps/api injects no keepalives)
|
|
287
|
+
// — this veto is deliberately NOT expired by `STREAM_OBSERVATION_MAX_MS`
|
|
288
|
+
// (a row still open past any time bound is more often a dropped
|
|
289
|
+
// `kind:"end"` relay than a live turn). The repair for the wrong case —
|
|
290
|
+
// a running turn behind a dead stream, prod 2026-08-26 — is EVIDENCE,
|
|
291
|
+
// not time: the transcript liveness poll stays on while the server holds
|
|
292
|
+
// a turn open (`livenessBusy`'s `serverHoldsTurn`), its runtime read
|
|
293
|
+
// stamps `sessionActivityAt` when the transcript moved with an open tail
|
|
294
|
+
// (`sync-store.hydrate`), and `activityAfterIdle` above then outranks
|
|
295
|
+
// this frame. Also note `started_at` is server clock while `atMs` is this
|
|
296
|
+
// tab's clock — skew can misclassify a NEW turn as ended, and the same
|
|
297
|
+
// evidence path is what recovers it.
|
|
298
|
+
return true;
|
|
299
|
+
};
|
|
300
|
+
if (activityAfterIdle) {
|
|
301
|
+
return {
|
|
302
|
+
state: 'working',
|
|
303
|
+
source: 'stream',
|
|
304
|
+
turnId: null,
|
|
305
|
+
since: activity.atMs,
|
|
306
|
+
serverOpenTurnToken: server?.turns[0]?.turn_token ?? null,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
// NOT gated on `serverFresh`. A read going stale is a fact about the READ, not
|
|
310
|
+
// about the row: the control plane does not release a turn because this tab's
|
|
311
|
+
// last look got old. Gating it here made being wrong self-sealing — the token
|
|
312
|
+
// nulls, `serverHoldsTurn` (use-session.ts) goes false, `livenessBusy` goes
|
|
313
|
+
// false, and the transcript fallback poll clears its own interval. The one
|
|
314
|
+
// mechanism that could have produced fresh evidence was switched off BY the
|
|
315
|
+
// staleness it existed to repair, so a false idle could never be discovered.
|
|
316
|
+
// The token is retired by end-of-turn evidence, never by age.
|
|
317
|
+
const ledgerTurn = server?.turns[0];
|
|
318
|
+
// `serverOpenTurnToken` deliberately keeps reporting the LEDGER's turn even
|
|
319
|
+
// when the runtime's frame has decided the state above — see its docstring.
|
|
320
|
+
// It answers "is the control plane still holding authority", which is what a
|
|
321
|
+
// `/` command must check before going straight at OpenCode with no admission
|
|
322
|
+
// gate in front of it, and that stays true for as long as the row does.
|
|
323
|
+
// Only the WORKING decision moves.
|
|
324
|
+
const serverOpenTurnToken = ledgerTurn?.turn_token ?? null;
|
|
325
|
+
// EVERY row, not just the first. The ledger holds more than one open turn
|
|
326
|
+
// whenever a prompt is forwarded while another is running — measured on the
|
|
327
|
+
// local stack as `turns: [B@00:28:56, A@00:28:22]` — and the list is not
|
|
328
|
+
// ordered newest-first. Testing only `turns[0]` would let one spent row hide
|
|
329
|
+
// a live one behind it, so the projection keeps the first turn the runtime
|
|
330
|
+
// has NOT finished.
|
|
331
|
+
const openTurn = serverFresh ? server.turns.find((t) => !endedByRuntime(t)) : undefined;
|
|
156
332
|
// A turn the authority is holding open, unless the stream has since said the
|
|
157
333
|
// session went idle. The stream frame is newer BY OBSERVATION, and the daemon
|
|
158
334
|
// relays `turn_end` to the control plane at the same moment the frame is
|
|
159
335
|
// emitted — so a fresher idle frame means this read is simply out of date.
|
|
160
|
-
|
|
336
|
+
//
|
|
337
|
+
// A LOCAL idle frame is exempt from that ordering: it is not the runtime
|
|
338
|
+
// speaking, so "newer" buys it nothing against the lifecycle authority. It
|
|
339
|
+
// lands between polls by construction (the sweep runs on connect), and
|
|
340
|
+
// waiting one poll interval for the row to outrank it again is exactly the
|
|
341
|
+
// flicker being fixed.
|
|
342
|
+
const streamContradicts = !!stream && !(stream.type === 'idle' && stream.origin === 'local');
|
|
343
|
+
if (openTurn && server.atMs >= abortFloor && (!streamContradicts || server.atMs >= stream.atMs)) {
|
|
161
344
|
return {
|
|
162
345
|
state: 'working',
|
|
163
346
|
source: 'server',
|
|
@@ -168,11 +351,18 @@ export function projectWorking(inputs) {
|
|
|
168
351
|
}
|
|
169
352
|
// Durable rows outrank an idle read: the read is right that no TURN is
|
|
170
353
|
// running and wrong that nothing is happening.
|
|
354
|
+
//
|
|
355
|
+
// Every live row counts, INCLUDING the ones already forwarded to the runtime.
|
|
356
|
+
// A prompt queued behind a running turn is handed to OpenCode early and sits
|
|
357
|
+
// in `delivering` ACROSS the turn boundary, so the idle frame that ends the
|
|
358
|
+
// turn in front of it says nothing about it. Measured on the local stack
|
|
359
|
+
// 2026-08-21: suppressing forwarded rows on that frame left the composer idle
|
|
360
|
+
// for 13.8s with the user's queued prompt still waiting to run.
|
|
171
361
|
if (inboxFresh && inbox.pending > 0) {
|
|
172
362
|
return {
|
|
173
363
|
state: 'working',
|
|
174
364
|
source: 'server',
|
|
175
|
-
turnId: null,
|
|
365
|
+
turnId: receiptLive ? receiptTurnId : null,
|
|
176
366
|
since: inbox.atMs,
|
|
177
367
|
serverOpenTurnToken,
|
|
178
368
|
};
|
|
@@ -201,7 +391,7 @@ export function projectWorking(inputs) {
|
|
|
201
391
|
return {
|
|
202
392
|
state: 'working',
|
|
203
393
|
source: 'optimistic',
|
|
204
|
-
turnId:
|
|
394
|
+
turnId: receiptTurnId,
|
|
205
395
|
since: optimistic.atMs,
|
|
206
396
|
serverOpenTurnToken,
|
|
207
397
|
};
|
|
@@ -241,13 +431,22 @@ export function projectWorking(inputs) {
|
|
|
241
431
|
* skipped, so re-arming from the timer this returns terminates.
|
|
242
432
|
*/
|
|
243
433
|
export function workingExpiryAtMs(inputs) {
|
|
244
|
-
const { optimistic, abort, inbox, server, stream, nowMs } = inputs;
|
|
434
|
+
const { optimistic, abort, inbox, server, stream, activity, nowMs } = inputs;
|
|
245
435
|
const deadlines = [
|
|
436
|
+
// The instant a runtime idle frame stops outranking the ledger's turn row.
|
|
437
|
+
// Nothing else re-renders then, and a session held `idle` by that rule has
|
|
438
|
+
// to go back to `working` on its own when the frame's window closes.
|
|
439
|
+
// (An idle frame no longer stops outranking the ledger on a timer — see
|
|
440
|
+
// `endedByRuntime` — so there is nothing to schedule for it here.)
|
|
441
|
+
null,
|
|
246
442
|
optimistic ? optimistic.atMs + OPTIMISTIC_RECEIPT_MAX_MS : null,
|
|
247
443
|
abort ? abort.atMs + OPTIMISTIC_ABORT_MAX_MS : null,
|
|
248
444
|
inbox ? inbox.atMs + INBOX_OBSERVATION_MAX_MS : null,
|
|
249
445
|
server ? server.atMs + SERVER_OBSERVATION_MAX_MS : null,
|
|
250
446
|
stream ? stream.atMs + STREAM_OBSERVATION_MAX_MS : null,
|
|
447
|
+
// Content stops answering when it goes stale, and nothing else re-renders
|
|
448
|
+
// at that instant.
|
|
449
|
+
activity ? activity.atMs + STREAM_OBSERVATION_MAX_MS : null,
|
|
251
450
|
];
|
|
252
451
|
let next = null;
|
|
253
452
|
for (const deadline of deadlines) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"working.js","sourceRoot":"","sources":["../../../src/core/session/working.ts"],"names":[],"mappings":"AAsDA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"working.js","sourceRoot":"","sources":["../../../src/core/session/working.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable per-session event log.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode's runtime keeps an ordered, replayable log per session, and exposes
|
|
5
|
+
* it on two routes that our sandboxes already serve (verified live through the
|
|
6
|
+
* Kortix proxy, dev, 2026-08-24):
|
|
7
|
+
*
|
|
8
|
+
* GET /api/session/{id}/history?limit&after -> { data: SessionDurableEvent[], hasMore }
|
|
9
|
+
* GET /api/session/{id}/event?after=<seq> -> SSE: replay, then tail
|
|
10
|
+
*
|
|
11
|
+
* The SDK's own words for the second one: "Replay durable events after an
|
|
12
|
+
* aggregate sequence, then continue with new durable events."
|
|
13
|
+
*
|
|
14
|
+
* Why this matters more than any repair we have written:
|
|
15
|
+
*
|
|
16
|
+
* Every transcript bug this file's neighbours exist to fix comes from the same
|
|
17
|
+
* shape — the browser holds a COPY assembled from a lossy stream, and has no
|
|
18
|
+
* way to ask "what did I miss?". So we invented answers: re-read the tail on a
|
|
19
|
+
* gap, on turn end, on tab return, on eviction; poll every 10s while working;
|
|
20
|
+
* infer "is it working" from six stamped observations. Each is a guess about
|
|
21
|
+
* loss we could not measure.
|
|
22
|
+
*
|
|
23
|
+
* A sequence cursor replaces all of it with a question the server can answer
|
|
24
|
+
* exactly: everything after `seq`. Missed nothing -> zero events. Missed a
|
|
25
|
+
* turn -> exactly that turn. Cost is proportional to what was MISSED rather
|
|
26
|
+
* than to the size of the transcript, which is the whole reason a 78 MB tail
|
|
27
|
+
* read could never be the right repair.
|
|
28
|
+
*
|
|
29
|
+
* Framework-free on purpose: no DOM, no React, no store. Callers own the cursor.
|
|
30
|
+
*/
|
|
31
|
+
/** One page of durable events. `limit` is per request, not a total. */
|
|
32
|
+
export declare const DURABLE_HISTORY_PAGE_SIZE = 200;
|
|
33
|
+
/** A durable event carries an aggregate sequence; a delta frame does not. */
|
|
34
|
+
export interface DurableEventLike {
|
|
35
|
+
durable?: {
|
|
36
|
+
aggregateID?: string;
|
|
37
|
+
seq?: number;
|
|
38
|
+
version?: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export interface DurableHistoryPage {
|
|
42
|
+
events: unknown[];
|
|
43
|
+
hasMore: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The cursor to pass as `after` next time. Never lower than the cursor this
|
|
46
|
+
* read was given — a rewind would replay events already applied.
|
|
47
|
+
*/
|
|
48
|
+
lastSeq: number | null;
|
|
49
|
+
}
|
|
50
|
+
export interface DurableLogClient {
|
|
51
|
+
v2?: {
|
|
52
|
+
session: {
|
|
53
|
+
history: (request: {
|
|
54
|
+
sessionID: string;
|
|
55
|
+
limit?: number;
|
|
56
|
+
after?: number;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
data?: {
|
|
59
|
+
data?: unknown[];
|
|
60
|
+
hasMore?: boolean;
|
|
61
|
+
};
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The aggregate sequence of an event, or null if it carries none.
|
|
68
|
+
*
|
|
69
|
+
* Only durable events advance the cursor. The runtime also emits pure deltas
|
|
70
|
+
* (text, reasoning, tool input, compaction) that exist to make typing look
|
|
71
|
+
* live; they are disposable by design and must never move a cursor, or a
|
|
72
|
+
* reconnect would skip the durable event they were painting over.
|
|
73
|
+
*/
|
|
74
|
+
export declare function durableSeqOf(event: unknown): number | null;
|
|
75
|
+
/**
|
|
76
|
+
* Read one page of the log after `after` (exclusive).
|
|
77
|
+
*
|
|
78
|
+
* The runtime's own note on this route: "Newly committed events may appear on
|
|
79
|
+
* later pages" — so `hasMore: false` means "caught up as of now", not "the log
|
|
80
|
+
* is finished". The caller keeps the cursor and asks again.
|
|
81
|
+
*/
|
|
82
|
+
export declare function readSessionDurableHistory(client: DurableLogClient, sessionId: string, cursor: {
|
|
83
|
+
after?: number;
|
|
84
|
+
limit?: number;
|
|
85
|
+
}): Promise<DurableHistoryPage>;
|
|
86
|
+
/**
|
|
87
|
+
* Does this failure mean "this runtime has no durable log" rather than "the
|
|
88
|
+
* read failed"?
|
|
89
|
+
*
|
|
90
|
+
* Self-hosted installs run older sandbox images. A 404 is a capability answer
|
|
91
|
+
* and must fall back to the message read. Everything else is a real failure and
|
|
92
|
+
* must reach the caller's retry — swallowing a 503 as "unsupported" would
|
|
93
|
+
* permanently downgrade a runtime that was merely asleep.
|
|
94
|
+
*/
|
|
95
|
+
export declare function isDurableLogUnsupported(error: unknown): boolean;
|
|
96
|
+
//# sourceMappingURL=durable-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-log.d.ts","sourceRoot":"","sources":["../../../src/core/session-sync/durable-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE;QACH,OAAO,EAAE;YACP,OAAO,EAAE,CAAC,OAAO,EAAE;gBACjB,SAAS,EAAE,MAAM,CAAC;gBAClB,KAAK,CAAC,EAAE,MAAM,CAAC;gBACf,KAAK,CAAC,EAAE,MAAM,CAAC;aAChB,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE;oBAAE,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;oBAAC,OAAO,CAAC,EAAE,OAAO,CAAA;iBAAE,CAAA;aAAE,CAAC,CAAC;SACnE,CAAC;KACH,CAAC;CACH;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1D;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,kBAAkB,CAAC,CAsB7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK/D"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable per-session event log.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode's runtime keeps an ordered, replayable log per session, and exposes
|
|
5
|
+
* it on two routes that our sandboxes already serve (verified live through the
|
|
6
|
+
* Kortix proxy, dev, 2026-08-24):
|
|
7
|
+
*
|
|
8
|
+
* GET /api/session/{id}/history?limit&after -> { data: SessionDurableEvent[], hasMore }
|
|
9
|
+
* GET /api/session/{id}/event?after=<seq> -> SSE: replay, then tail
|
|
10
|
+
*
|
|
11
|
+
* The SDK's own words for the second one: "Replay durable events after an
|
|
12
|
+
* aggregate sequence, then continue with new durable events."
|
|
13
|
+
*
|
|
14
|
+
* Why this matters more than any repair we have written:
|
|
15
|
+
*
|
|
16
|
+
* Every transcript bug this file's neighbours exist to fix comes from the same
|
|
17
|
+
* shape — the browser holds a COPY assembled from a lossy stream, and has no
|
|
18
|
+
* way to ask "what did I miss?". So we invented answers: re-read the tail on a
|
|
19
|
+
* gap, on turn end, on tab return, on eviction; poll every 10s while working;
|
|
20
|
+
* infer "is it working" from six stamped observations. Each is a guess about
|
|
21
|
+
* loss we could not measure.
|
|
22
|
+
*
|
|
23
|
+
* A sequence cursor replaces all of it with a question the server can answer
|
|
24
|
+
* exactly: everything after `seq`. Missed nothing -> zero events. Missed a
|
|
25
|
+
* turn -> exactly that turn. Cost is proportional to what was MISSED rather
|
|
26
|
+
* than to the size of the transcript, which is the whole reason a 78 MB tail
|
|
27
|
+
* read could never be the right repair.
|
|
28
|
+
*
|
|
29
|
+
* Framework-free on purpose: no DOM, no React, no store. Callers own the cursor.
|
|
30
|
+
*/
|
|
31
|
+
/** One page of durable events. `limit` is per request, not a total. */
|
|
32
|
+
export const DURABLE_HISTORY_PAGE_SIZE = 200;
|
|
33
|
+
/**
|
|
34
|
+
* The aggregate sequence of an event, or null if it carries none.
|
|
35
|
+
*
|
|
36
|
+
* Only durable events advance the cursor. The runtime also emits pure deltas
|
|
37
|
+
* (text, reasoning, tool input, compaction) that exist to make typing look
|
|
38
|
+
* live; they are disposable by design and must never move a cursor, or a
|
|
39
|
+
* reconnect would skip the durable event they were painting over.
|
|
40
|
+
*/
|
|
41
|
+
export function durableSeqOf(event) {
|
|
42
|
+
if (!event || typeof event !== 'object')
|
|
43
|
+
return null;
|
|
44
|
+
const durable = event.durable;
|
|
45
|
+
if (!durable || typeof durable.seq !== 'number' || !Number.isFinite(durable.seq))
|
|
46
|
+
return null;
|
|
47
|
+
return durable.seq;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read one page of the log after `after` (exclusive).
|
|
51
|
+
*
|
|
52
|
+
* The runtime's own note on this route: "Newly committed events may appear on
|
|
53
|
+
* later pages" — so `hasMore: false` means "caught up as of now", not "the log
|
|
54
|
+
* is finished". The caller keeps the cursor and asks again.
|
|
55
|
+
*/
|
|
56
|
+
export async function readSessionDurableHistory(client, sessionId, cursor) {
|
|
57
|
+
const history = client.v2?.session.history;
|
|
58
|
+
if (!history)
|
|
59
|
+
throw new Error('durable log unavailable: client exposes no v2 session namespace');
|
|
60
|
+
const result = await history({
|
|
61
|
+
sessionID: sessionId,
|
|
62
|
+
limit: cursor.limit ?? DURABLE_HISTORY_PAGE_SIZE,
|
|
63
|
+
...(cursor.after === undefined ? {} : { after: cursor.after }),
|
|
64
|
+
});
|
|
65
|
+
const events = result.data?.data ?? [];
|
|
66
|
+
// The HIGHEST sequence in the page, not the last element's. A page is not
|
|
67
|
+
// promised in sequence order, and it may carry frames with no sequence at
|
|
68
|
+
// all; taking the tail element would rewind the cursor on either.
|
|
69
|
+
let lastSeq = cursor.after ?? null;
|
|
70
|
+
for (const event of events) {
|
|
71
|
+
const seq = durableSeqOf(event);
|
|
72
|
+
if (seq === null)
|
|
73
|
+
continue;
|
|
74
|
+
if (lastSeq === null || seq > lastSeq)
|
|
75
|
+
lastSeq = seq;
|
|
76
|
+
}
|
|
77
|
+
return { events, hasMore: result.data?.hasMore === true, lastSeq };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Does this failure mean "this runtime has no durable log" rather than "the
|
|
81
|
+
* read failed"?
|
|
82
|
+
*
|
|
83
|
+
* Self-hosted installs run older sandbox images. A 404 is a capability answer
|
|
84
|
+
* and must fall back to the message read. Everything else is a real failure and
|
|
85
|
+
* must reach the caller's retry — swallowing a 503 as "unsupported" would
|
|
86
|
+
* permanently downgrade a runtime that was merely asleep.
|
|
87
|
+
*/
|
|
88
|
+
export function isDurableLogUnsupported(error) {
|
|
89
|
+
if (!error || typeof error !== 'object')
|
|
90
|
+
return false;
|
|
91
|
+
const status = error.status ?? error.statusCode;
|
|
92
|
+
return status === 404;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=durable-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-log.js","sourceRoot":"","sources":["../../../src/core/session-sync/durable-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AA6B7C;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,OAAO,GAAI,KAA0B,CAAC,OAAO,CAAC;IACpD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9F,OAAO,OAAO,CAAC,GAAG,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAwB,EACxB,SAAiB,EACjB,MAA0C;IAE1C,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC;IAC3C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IAEjG,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;QAC3B,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,yBAAyB;QAChD,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;KAC/D,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;IACvC,0EAA0E;IAC1E,0EAA0E;IAC1E,kEAAkE;IAClE,IAAI,OAAO,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,GAAG,KAAK,IAAI;YAAE,SAAS;QAC3B,IAAI,OAAO,KAAK,IAAI,IAAI,GAAG,GAAG,OAAO;YAAE,OAAO,GAAG,GAAG,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC;AACrE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GACT,KAA8B,CAAC,MAAM,IAAK,KAAkC,CAAC,UAAU,CAAC;IAC3F,OAAO,MAAM,KAAK,GAAG,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this session's transcript a FRAGMENT — messages the live stream rebuilt
|
|
3
|
+
* after an eviction, with everything before them missing?
|
|
4
|
+
*
|
|
5
|
+
* The sync store already carries the exact signature, in two pieces:
|
|
6
|
+
*
|
|
7
|
+
* - eviction drops a detached session's messages and MARKS the id
|
|
8
|
+
* - every path that re-establishes the session authoritatively — `hydrate`,
|
|
9
|
+
* `clearSession`, `optimisticAdd` — CLEARS that mark
|
|
10
|
+
* - `applyEvent`, the live stream's own path, does not
|
|
11
|
+
*
|
|
12
|
+
* So messages present while the mark is still set can only have come from
|
|
13
|
+
* frames that arrived after the eviction. The transcript starts mid-conversation
|
|
14
|
+
* and nothing else will correct it: the mount already ran, so no `initial` read
|
|
15
|
+
* is coming, and the liveness poll only runs while the session is working.
|
|
16
|
+
*
|
|
17
|
+
* Named and left open by 5a7a43517f, which removed the IndexedDB mirror that
|
|
18
|
+
* used to repaint underneath such a fragment: "no reconcile is keyed on
|
|
19
|
+
* eviction … can sit on a partial transcript until a reload."
|
|
20
|
+
*/
|
|
21
|
+
export declare function transcriptIsFragment(input: {
|
|
22
|
+
/** The store holds at least one message for this session. */
|
|
23
|
+
hasMessages: boolean;
|
|
24
|
+
/** The store still marks this session's transcript as evicted. */
|
|
25
|
+
wasEvicted: boolean;
|
|
26
|
+
}): boolean;
|
|
27
|
+
//# sourceMappingURL=fragment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../../src/core/session-sync/fragment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAEV"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this session's transcript a FRAGMENT — messages the live stream rebuilt
|
|
3
|
+
* after an eviction, with everything before them missing?
|
|
4
|
+
*
|
|
5
|
+
* The sync store already carries the exact signature, in two pieces:
|
|
6
|
+
*
|
|
7
|
+
* - eviction drops a detached session's messages and MARKS the id
|
|
8
|
+
* - every path that re-establishes the session authoritatively — `hydrate`,
|
|
9
|
+
* `clearSession`, `optimisticAdd` — CLEARS that mark
|
|
10
|
+
* - `applyEvent`, the live stream's own path, does not
|
|
11
|
+
*
|
|
12
|
+
* So messages present while the mark is still set can only have come from
|
|
13
|
+
* frames that arrived after the eviction. The transcript starts mid-conversation
|
|
14
|
+
* and nothing else will correct it: the mount already ran, so no `initial` read
|
|
15
|
+
* is coming, and the liveness poll only runs while the session is working.
|
|
16
|
+
*
|
|
17
|
+
* Named and left open by 5a7a43517f, which removed the IndexedDB mirror that
|
|
18
|
+
* used to repaint underneath such a fragment: "no reconcile is keyed on
|
|
19
|
+
* eviction … can sit on a partial transcript until a reload."
|
|
20
|
+
*/
|
|
21
|
+
export function transcriptIsFragment(input) {
|
|
22
|
+
return input.hasMessages && input.wasEvicted;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=fragment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../../src/core/session-sync/fragment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAKpC;IACC,OAAO,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;AAC/C,CAAC"}
|