@astralform/js 7.5.0 → 7.5.1

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/index.cjs CHANGED
@@ -2938,23 +2938,41 @@ var StreamManager = class {
2938
2938
  }
2939
2939
  }
2940
2940
  // ── Internal: restore ─────────────────────────────────────────
2941
+ /**
2942
+ * A conversation's turns, oldest first.
2943
+ *
2944
+ * Its own method so ``restore`` can put the request on the wire beside the
2945
+ * probe and the message list while ``replayHistory``, which consumes it,
2946
+ * keeps owning the shape it reads.
2947
+ */
2948
+ jobList(conversationId) {
2949
+ return this.session.client.get(
2950
+ `/v1/conversations/${encodeURIComponent(conversationId)}/jobs`
2951
+ );
2952
+ }
2941
2953
  async restore(conversationId, gen) {
2942
2954
  const superseded = () => gen !== this.generation;
2943
2955
  if (superseded()) return;
2944
2956
  const turn = this.turnCounter;
2945
2957
  const announcedRestoring = !this.session.isStreaming;
2946
2958
  if (announcedRestoring) this.setState("restoring");
2947
- let activeJobId = null;
2948
- try {
2949
- const res = await this.session.client.getActiveJob(conversationId);
2950
- activeJobId = res.jobId;
2951
- } catch {
2952
- }
2953
2959
  if (superseded()) return;
2954
- await this.session.loadConversation(conversationId);
2960
+ const probeRequest = this.session.client.getActiveJob(conversationId).catch(() => null);
2961
+ const loadRequest = this.session.loadConversation(conversationId);
2962
+ const jobsRequest = announcedRestoring ? this.jobList(conversationId) : null;
2963
+ void jobsRequest?.catch(() => {
2964
+ });
2965
+ const [probe] = await Promise.all([probeRequest, loadRequest]);
2966
+ const activeJobId = probe?.jobId ?? null;
2955
2967
  if (superseded()) return;
2956
2968
  if (announcedRestoring && this.viewTakenOverByLiveTurn()) return;
2957
- if (announcedRestoring && !await this.replayHistory(conversationId, gen, activeJobId, turn))
2969
+ if (jobsRequest && !await this.replayHistory(
2970
+ conversationId,
2971
+ gen,
2972
+ activeJobId,
2973
+ turn,
2974
+ jobsRequest
2975
+ ))
2958
2976
  return;
2959
2977
  if (activeJobId) {
2960
2978
  this.setState("streaming");
@@ -3012,11 +3030,17 @@ var StreamManager = class {
3012
3030
  * that started it, which is emitted as a bubble with no events — the whole
3013
3031
  * reason a conversation reopened mid-turn now shows the message that started
3014
3032
  * that turn.
3033
+ *
3034
+ * ``jobsRequest`` is the job list already IN FLIGHT — issued by ``restore``
3035
+ * alongside the probe and the message list rather than fetched here, so the
3036
+ * three round trips overlap. It is awaited inside the try below, which is
3037
+ * what keeps a failed job list non-blocking exactly as it was when the fetch
3038
+ * lived here.
3015
3039
  */
3016
- async replayHistory(conversationId, gen, activeJobId, turn) {
3040
+ async replayHistory(conversationId, gen, activeJobId, turn, jobsRequest) {
3017
3041
  const stopReplay = () => gen !== this.generation || this.viewTakenOverByLiveTurn() || this.turnStarted(turn);
3018
3042
  try {
3019
- const jobs = await this.session.client.get(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);
3043
+ const jobs = await jobsRequest;
3020
3044
  if (stopReplay()) return false;
3021
3045
  const replayableJobs = jobs.filter(
3022
3046
  (j) => j.job_id !== activeJobId