@kortix/sdk 0.13.4 → 0.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/session-sync-registry.d.ts +20 -0
- package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
- package/dist/browser/session-sync/session-sync-registry.js +69 -6
- 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.js +1 -1
- package/dist/browser/stores/sandbox-connection-store.js.map +1 -1
- package/dist/browser/stores/sync-store.d.ts +27 -1
- package/dist/browser/stores/sync-store.d.ts.map +1 -1
- package/dist/browser/stores/sync-store.js +198 -41
- package/dist/browser/stores/sync-store.js.map +1 -1
- package/dist/core/client/kortix.d.ts +0 -11
- package/dist/core/client/kortix.d.ts.map +1 -1
- package/dist/core/client/kortix.js +14 -8
- package/dist/core/client/kortix.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/index.d.ts +0 -2
- package/dist/core/rest/projects-client/index.d.ts.map +1 -1
- package/dist/core/rest/projects-client/index.js +0 -2
- package/dist/core/rest/projects-client/index.js.map +1 -1
- package/dist/core/rest/projects-client/projects.d.ts +1 -1
- package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
- package/dist/core/rest/projects-client/projects.js +5 -3
- package/dist/core/rest/projects-client/projects.js.map +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +8 -33
- package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
- package/dist/core/rest/projects-client/sessions.js +1 -18
- 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 +1 -0
- package/dist/core/session/index.d.ts.map +1 -1
- package/dist/core/session/index.js +1 -0
- package/dist/core/session/index.js.map +1 -1
- 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/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/working.d.ts +44 -0
- package/dist/core/session/working.d.ts.map +1 -1
- package/dist/core/session/working.js +146 -4
- 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 +83 -9
- package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
- package/dist/core/session-sync/session-sync-controller.js +183 -17
- package/dist/core/session-sync/session-sync-controller.js.map +1 -1
- package/dist/core/turns/grouping.d.ts.map +1 -1
- package/dist/core/turns/grouping.js +60 -23
- package/dist/core/turns/grouping.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kortix.esm.min.js +24 -24
- package/dist/kortix.global.js +24 -24
- 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/opencode.d.ts +1 -0
- package/dist/react/opencode.d.ts.map +1 -1
- package/dist/react/opencode.js +3 -0
- package/dist/react/opencode.js.map +1 -1
- package/dist/react/use-admin-accounts.d.ts +35 -0
- package/dist/react/use-admin-accounts.d.ts.map +1 -1
- package/dist/react/use-admin-accounts.js +18 -0
- package/dist/react/use-admin-accounts.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/index.d.ts.map +1 -1
- package/dist/react/use-opencode-events/index.js +59 -6
- 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-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 +12 -2
- 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-session-prompts.d.ts +37 -2
- package/dist/react/use-session-prompts.d.ts.map +1 -1
- package/dist/react/use-session-prompts.js +62 -15
- 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 +40 -4
- package/dist/react/use-session-send.js.map +1 -1
- package/dist/react/use-session-sync.d.ts +13 -1
- package/dist/react/use-session-sync.d.ts.map +1 -1
- package/dist/react/use-session-sync.js +81 -55
- package/dist/react/use-session-sync.js.map +1 -1
- package/dist/react/use-session-working.d.ts +17 -0
- package/dist/react/use-session-working.d.ts.map +1 -1
- package/dist/react/use-session-working.js +33 -3
- package/dist/react/use-session-working.js.map +1 -1
- package/package.json +3 -3
- 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
|
@@ -92,6 +92,30 @@ export declare const STREAM_OBSERVATION_MAX_MS = 45000;
|
|
|
92
92
|
* Stop with its reply on screen for up to 45s.
|
|
93
93
|
*/
|
|
94
94
|
export declare const INBOX_OBSERVATION_MAX_MS = 10000;
|
|
95
|
+
/**
|
|
96
|
+
* How long a runtime `idle` frame may outrank the control plane's turn row.
|
|
97
|
+
*
|
|
98
|
+
* The ledger is closed by a SEPARATE daemon relay (`POST .../turn-stream`
|
|
99
|
+
* `kind:"end"`), so for a moment after the runtime goes idle the row is still
|
|
100
|
+
* open and a `/turn` read taken in that window honestly reports a turn that has
|
|
101
|
+
* finished. MEASURED 2026-08-21 on a healthy stack: the relay POST itself took
|
|
102
|
+
* 1765ms, while the idle frame reaches the tab immediately and triggers its own
|
|
103
|
+
* refetch that lands ~186ms later — squarely inside the gap. Without this the
|
|
104
|
+
* composer's Stop button and the turn shimmer came back ~200ms after the reply
|
|
105
|
+
* and stayed for the whole poll interval.
|
|
106
|
+
*
|
|
107
|
+
* BOUNDED, and this is the important half. An idle frame is NOT proof the turn
|
|
108
|
+
* ended: `session.error` is relayed as `idle` too ("errors terminate the
|
|
109
|
+
* response"), and OpenCode emits it while it is RETRYING a provider — a 429
|
|
110
|
+
* backoff, a transient upstream 5xx — with the turn still very much live. An
|
|
111
|
+
* unbounded rule therefore un-busied the composer mid-turn on every retry and
|
|
112
|
+
* held it that way until the next `busy` frame, which is a worse flicker than
|
|
113
|
+
* the one it fixes. Past this window the ledger is believed again, so a retry
|
|
114
|
+
* blip self-heals in seconds instead of lasting the stream's whole 45s life.
|
|
115
|
+
*
|
|
116
|
+
* Comfortably above the measured relay, far below a provider backoff.
|
|
117
|
+
*/
|
|
118
|
+
export declare const TURN_END_LEDGER_LAG_MS = 3000;
|
|
95
119
|
/**
|
|
96
120
|
* How long a stop this tab issued may bar a server read from reporting the
|
|
97
121
|
* turn it is ending.
|
|
@@ -159,6 +183,24 @@ export interface WorkingStreamInput {
|
|
|
159
183
|
type: 'busy' | 'retry' | 'idle';
|
|
160
184
|
atMs: number;
|
|
161
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* The last moment the RUNTIME'S OWN OUTPUT reached this tab for this session —
|
|
188
|
+
* a streamed part, a message. Stamped on observation, like every other input.
|
|
189
|
+
*
|
|
190
|
+
* Every other input here is an OBSERVER of the runtime: a `/turn` poll, a
|
|
191
|
+
* status frame, a health probe, an inbox read. Each can be dropped, throttled
|
|
192
|
+
* or aged out, and when one is, the projection has historically had to guess.
|
|
193
|
+
* This one is not an observer. Content arriving is not a report that the
|
|
194
|
+
* runtime is working — it IS the runtime working, and no report outranks it.
|
|
195
|
+
*
|
|
196
|
+
* Reported with a screen recording (essentia, 2026-08-23): a tool row with a
|
|
197
|
+
* live spinner and text growing on screen, and a composer showing its send
|
|
198
|
+
* arrow. Every observer had gone quiet; the only thing still speaking was the
|
|
199
|
+
* content, and nothing was listening to it.
|
|
200
|
+
*/
|
|
201
|
+
export interface WorkingActivityInput {
|
|
202
|
+
atMs: number;
|
|
203
|
+
}
|
|
162
204
|
/** One read of the session's durable prompt inbox: how many rows the server is
|
|
163
205
|
* still going to run, and when that list was read. */
|
|
164
206
|
export interface WorkingInboxInput {
|
|
@@ -173,6 +215,8 @@ export interface WorkingInputs {
|
|
|
173
215
|
inbox?: WorkingInboxInput | null;
|
|
174
216
|
server: WorkingServerInput | null;
|
|
175
217
|
stream: WorkingStreamInput | null;
|
|
218
|
+
/** The runtime's own output, last seen. See `WorkingActivityInput`. */
|
|
219
|
+
activity?: WorkingActivityInput | null;
|
|
176
220
|
nowMs: number;
|
|
177
221
|
}
|
|
178
222
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"working.d.ts","sourceRoot":"","sources":["../../../src/core/session/working.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,gBAAgB,EACjB,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,wEAAwE;IACxE,MAAM,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B;gEAC4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;iFACiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;uDACuD;AACvD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,uEAAuE;IACvE,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAO/E;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"working.d.ts","sourceRoot":"","sources":["../../../src/core/session/working.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.
|
|
@@ -119,7 +148,7 @@ function instant(value) {
|
|
|
119
148
|
* must re-evaluate at those instants — see `workingExpiryAtMs`.
|
|
120
149
|
*/
|
|
121
150
|
export function projectWorking(inputs) {
|
|
122
|
-
const { optimistic, abort, inbox, server, stream, nowMs } = inputs;
|
|
151
|
+
const { optimistic, abort, inbox, server, stream, activity, nowMs } = inputs;
|
|
123
152
|
const receiptLive = !!optimistic && nowMs - optimistic.atMs < OPTIMISTIC_RECEIPT_MAX_MS;
|
|
124
153
|
// TWO floors, because the two server-side observers have different knowledge.
|
|
125
154
|
//
|
|
@@ -151,8 +180,105 @@ export function projectWorking(inputs) {
|
|
|
151
180
|
const serverFresh = !!server && nowMs - server.atMs <= SERVER_OBSERVATION_MAX_MS;
|
|
152
181
|
const streamFresh = !!stream && nowMs - stream.atMs <= STREAM_OBSERVATION_MAX_MS;
|
|
153
182
|
const inboxFresh = !!inbox && nowMs - inbox.atMs <= INBOX_OBSERVATION_MAX_MS;
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
// The runtime's own end-of-turn frame.
|
|
184
|
+
//
|
|
185
|
+
// NOT gated on `streamFresh`, unlike every branch that decides `working` from
|
|
186
|
+
// the stream. The bound exists because an old frame cannot testify to what is
|
|
187
|
+
// happening NOW — but this frame is not asked that. It is asked whether a turn
|
|
188
|
+
// that started BEFORE it has ended, and that answer does not rot: a turn which
|
|
189
|
+
// resumed would have produced a newer, non-idle frame, and then this is not an
|
|
190
|
+
// idle frame at all. Gating it cost exactly what the 3s window cost, 42s later
|
|
191
|
+
// and permanently — at `stream.atMs + STREAM_OBSERVATION_MAX_MS` the veto
|
|
192
|
+
// vanished with no new input, and a row the relay never closed put the
|
|
193
|
+
// composer back on Stop until its deadline (240 MINUTES for an accepted turn).
|
|
194
|
+
const idleFrame = stream && stream.type === 'idle' ? stream : null;
|
|
195
|
+
// CONTENT FIRST. Bounded by the stream's own freshness rule, because it
|
|
196
|
+
// arrives on the same transport and goes stale for the same reasons — but
|
|
197
|
+
// within that window it outranks every observer, including an idle frame it
|
|
198
|
+
// postdates. A runtime that is emitting parts is working, whatever the last
|
|
199
|
+
// status frame said and whatever a poll that has not answered yet will say.
|
|
200
|
+
const activityFresh = !!activity && nowMs - activity.atMs <= STREAM_OBSERVATION_MAX_MS;
|
|
201
|
+
const activityAfterIdle = activityFresh && (!idleFrame || activity.atMs > idleFrame.atMs);
|
|
202
|
+
/**
|
|
203
|
+
* Whether the runtime has already finished this turn.
|
|
204
|
+
*
|
|
205
|
+
* "Newer read wins" is the rule this replaces, and it is wrong here, because
|
|
206
|
+
* the two observers do not learn the same fact at the same time. The idle
|
|
207
|
+
* frame comes straight off the runtime over SSE. The ledger row is closed by
|
|
208
|
+
* a SEPARATE daemon relay (`POST .../turn-stream` `kind:"end"`) — so a `/turn`
|
|
209
|
+
* read ISSUED after the frame is still ABOUT a turn the frame already ended.
|
|
210
|
+
*
|
|
211
|
+
* MEASURED, local stack 2026-08-21, one ordinary composer turn: the idle
|
|
212
|
+
* frame reached the tab at 00:03:59.964, the refetch that frame itself
|
|
213
|
+
* triggers landed at 00:04:00.150 stamped 44ms later and still reported the
|
|
214
|
+
* turn `active`, and the ledger did not record `ended_at` until 00:04:15.132
|
|
215
|
+
* — the relay for that turn never arrived and a reconciliation sweep closed
|
|
216
|
+
* it 15.1s late. The composer's Stop button and the turn's shimmer came back
|
|
217
|
+
* 186ms after they left and stayed for fifteen seconds, with the finished
|
|
218
|
+
* reply already on screen. Even in the healthy case the relay lands ~200ms
|
|
219
|
+
* after the frame, which is still inside the window its own refetch lands in.
|
|
220
|
+
*
|
|
221
|
+
* `started_at` is what separates the two turns the rule has to tell apart: a
|
|
222
|
+
* turn that began BEFORE the frame is the turn that frame ended, and a turn
|
|
223
|
+
* that began after it is a NEW one the frame knows nothing about — a queued
|
|
224
|
+
* prompt draining, a trigger firing, a second device sending. That one keeps
|
|
225
|
+
* the ledger's full authority, with no delay and no window.
|
|
226
|
+
*
|
|
227
|
+
* A row with no start instant (a legacy `activeTurn`) cannot be ranked
|
|
228
|
+
* against the frame at all, and inventing an order there would hide a live
|
|
229
|
+
* turn. The ledger keeps it.
|
|
230
|
+
*/
|
|
231
|
+
const endedByRuntime = (candidate) => {
|
|
232
|
+
if (!idleFrame)
|
|
233
|
+
return false;
|
|
234
|
+
// Only a turn that began BEFORE the frame — a turn started after it is a
|
|
235
|
+
// new one the frame knows nothing about.
|
|
236
|
+
const startedAt = instant(candidate.started_at);
|
|
237
|
+
if (startedAt === null || startedAt >= idleFrame.atMs)
|
|
238
|
+
return false;
|
|
239
|
+
// And that is the whole rule. It used to expire after
|
|
240
|
+
// `TURN_END_LEDGER_LAG_MS`, on the theory that a row still open past the
|
|
241
|
+
// relay's lag must mean the frame was a retry's `session.error` rather than
|
|
242
|
+
// the end of anything. But time is not evidence: when the `kind:"end"`
|
|
243
|
+
// relay is simply DROPPED — the documented failure mode, closed by a
|
|
244
|
+
// reconciliation sweep 15.1s late in this file's own measurement — the row
|
|
245
|
+
// stays open for exactly the same reason and the turn is exactly as
|
|
246
|
+
// finished. Expiring the veto therefore announced a finished turn again,
|
|
247
|
+
// and the user watched "Gathering thoughts…" and the Stop button come back
|
|
248
|
+
// for seconds with the answer already on screen (dev, 2026-08-23).
|
|
249
|
+
//
|
|
250
|
+
// A turn that is genuinely still running says so, and that is what returns
|
|
251
|
+
// authority to the ledger: any newer frame is not idle, so `idleFrame` is
|
|
252
|
+
// null on the next evaluation and the row decides again — immediately, with
|
|
253
|
+
// no window to tune. A runtime that goes silent instead is bounded by
|
|
254
|
+
// `STREAM_OBSERVATION_MAX_MS`, after which the frame is too stale to veto
|
|
255
|
+
// anything.
|
|
256
|
+
return true;
|
|
257
|
+
};
|
|
258
|
+
if (activityAfterIdle) {
|
|
259
|
+
return {
|
|
260
|
+
state: 'working',
|
|
261
|
+
source: 'stream',
|
|
262
|
+
turnId: null,
|
|
263
|
+
since: activity.atMs,
|
|
264
|
+
serverOpenTurnToken: serverFresh ? (server.turns[0]?.turn_token ?? null) : null,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const ledgerTurn = serverFresh ? server.turns[0] : undefined;
|
|
268
|
+
// `serverOpenTurnToken` deliberately keeps reporting the LEDGER's turn even
|
|
269
|
+
// when the runtime's frame has decided the state above — see its docstring.
|
|
270
|
+
// It answers "is the control plane still holding authority", which is what a
|
|
271
|
+
// `/` command must check before going straight at OpenCode with no admission
|
|
272
|
+
// gate in front of it, and that stays true for as long as the row does.
|
|
273
|
+
// Only the WORKING decision moves.
|
|
274
|
+
const serverOpenTurnToken = ledgerTurn?.turn_token ?? null;
|
|
275
|
+
// EVERY row, not just the first. The ledger holds more than one open turn
|
|
276
|
+
// whenever a prompt is forwarded while another is running — measured on the
|
|
277
|
+
// local stack as `turns: [B@00:28:56, A@00:28:22]` — and the list is not
|
|
278
|
+
// ordered newest-first. Testing only `turns[0]` would let one spent row hide
|
|
279
|
+
// a live one behind it, so the projection keeps the first turn the runtime
|
|
280
|
+
// has NOT finished.
|
|
281
|
+
const openTurn = serverFresh ? server.turns.find((t) => !endedByRuntime(t)) : undefined;
|
|
156
282
|
// A turn the authority is holding open, unless the stream has since said the
|
|
157
283
|
// session went idle. The stream frame is newer BY OBSERVATION, and the daemon
|
|
158
284
|
// relays `turn_end` to the control plane at the same moment the frame is
|
|
@@ -168,6 +294,13 @@ export function projectWorking(inputs) {
|
|
|
168
294
|
}
|
|
169
295
|
// Durable rows outrank an idle read: the read is right that no TURN is
|
|
170
296
|
// running and wrong that nothing is happening.
|
|
297
|
+
//
|
|
298
|
+
// Every live row counts, INCLUDING the ones already forwarded to the runtime.
|
|
299
|
+
// A prompt queued behind a running turn is handed to OpenCode early and sits
|
|
300
|
+
// in `delivering` ACROSS the turn boundary, so the idle frame that ends the
|
|
301
|
+
// turn in front of it says nothing about it. Measured on the local stack
|
|
302
|
+
// 2026-08-21: suppressing forwarded rows on that frame left the composer idle
|
|
303
|
+
// for 13.8s with the user's queued prompt still waiting to run.
|
|
171
304
|
if (inboxFresh && inbox.pending > 0) {
|
|
172
305
|
return {
|
|
173
306
|
state: 'working',
|
|
@@ -241,13 +374,22 @@ export function projectWorking(inputs) {
|
|
|
241
374
|
* skipped, so re-arming from the timer this returns terminates.
|
|
242
375
|
*/
|
|
243
376
|
export function workingExpiryAtMs(inputs) {
|
|
244
|
-
const { optimistic, abort, inbox, server, stream, nowMs } = inputs;
|
|
377
|
+
const { optimistic, abort, inbox, server, stream, activity, nowMs } = inputs;
|
|
245
378
|
const deadlines = [
|
|
379
|
+
// The instant a runtime idle frame stops outranking the ledger's turn row.
|
|
380
|
+
// Nothing else re-renders then, and a session held `idle` by that rule has
|
|
381
|
+
// to go back to `working` on its own when the frame's window closes.
|
|
382
|
+
// (An idle frame no longer stops outranking the ledger on a timer — see
|
|
383
|
+
// `endedByRuntime` — so there is nothing to schedule for it here.)
|
|
384
|
+
null,
|
|
246
385
|
optimistic ? optimistic.atMs + OPTIMISTIC_RECEIPT_MAX_MS : null,
|
|
247
386
|
abort ? abort.atMs + OPTIMISTIC_ABORT_MAX_MS : null,
|
|
248
387
|
inbox ? inbox.atMs + INBOX_OBSERVATION_MAX_MS : null,
|
|
249
388
|
server ? server.atMs + SERVER_OBSERVATION_MAX_MS : null,
|
|
250
389
|
stream ? stream.atMs + STREAM_OBSERVATION_MAX_MS : null,
|
|
390
|
+
// Content stops answering when it goes stale, and nothing else re-renders
|
|
391
|
+
// at that instant.
|
|
392
|
+
activity ? activity.atMs + STREAM_OBSERVATION_MAX_MS : null,
|
|
251
393
|
];
|
|
252
394
|
let next = null;
|
|
253
395
|
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"}
|