@astralform/js 6.0.1 → 6.0.3

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
@@ -2259,6 +2259,13 @@ function planRestore(args) {
2259
2259
  content: msgs[i]?.content,
2260
2260
  messageId: msgs[i]?.id
2261
2261
  }));
2262
+ if (jobs.length === 0) {
2263
+ return userMessages.map((m) => ({
2264
+ kind: "steer",
2265
+ content: m.content,
2266
+ messageId: m.id
2267
+ }));
2268
+ }
2262
2269
  const firstLinked = jobs.findIndex((j) => linkOf(j) !== void 0);
2263
2270
  if (firstLinked === -1) {
2264
2271
  return positional(jobs, userMessages);
@@ -2751,15 +2758,15 @@ var StreamManager = class {
2751
2758
  try {
2752
2759
  const jobs = await this.session.client.get(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);
2753
2760
  if (stopReplay()) return false;
2754
- const completedJobs = jobs.filter(
2755
- (j) => j.status === "completed" && j.job_id !== activeJobId
2761
+ const replayableJobs = jobs.filter(
2762
+ (j) => j.job_id !== activeJobId
2756
2763
  );
2757
2764
  const userMessages = this.session.messages.filter(
2758
2765
  (m) => m.role === "user"
2759
2766
  );
2760
2767
  const runningJob = activeJobId ? jobs.find((j) => j.job_id === activeJobId) : void 0;
2761
2768
  const plan = planRestore({
2762
- completedJobs: completedJobs.map((j) => ({
2769
+ completedJobs: replayableJobs.map((j) => ({
2763
2770
  job_id: j.job_id,
2764
2771
  message_id: j.message_id
2765
2772
  })),
@@ -2767,27 +2774,38 @@ var StreamManager = class {
2767
2774
  job_id: runningJob.job_id,
2768
2775
  message_id: runningJob.message_id
2769
2776
  },
2770
- // Completed jobs PLUS the ones still going. A send landing in the
2771
- // probe window has a running job, so its prompt is claimed and does
2772
- // not read as a steer replayed over the bubble the live send already
2773
- // rendered. Failed and cancelled jobs are deliberately NOT claimed:
2774
- // they produce no `turn` step, so claiming them would delete the
2775
- // user's prompt from the restore entirely rather than show it as a
2776
- // steer.
2777
- claimedMessageIds: jobs.filter((j) => j.status !== "failed" && j.status !== "cancelled").map((j) => j.message_id),
2777
+ // EVERY job, which is the same set the replay walk gets. The claim set
2778
+ // answers one question "will some turn step already draw this prompt?"
2779
+ // so it is a RESTATEMENT of the walk, and the two drifting apart is
2780
+ // what produces either a missing bubble or a doubled one.
2781
+ //
2782
+ // Failed and cancelled were excluded here for exactly one reason: they
2783
+ // produced no turn step, which is the bug fixed above. Now that they do,
2784
+ // the exclusion has no case left to describe.
2785
+ //
2786
+ // Being precise about what this change does and does not do: it is not
2787
+ // load-bearing TODAY. `planRestore` anchors a prompt at the index its
2788
+ // job links to and advances the cursor past it, so a message claimed by
2789
+ // a job inside the walk is never offered to the steer branch anyway —
2790
+ // the two spellings agree on current inputs. It is here because the
2791
+ // invariant is what keeps them agreeing: narrow the walk again without
2792
+ // narrowing this, and the prompts of the jobs dropped from it go with
2793
+ // them, silently. Deriving both from `jobs` makes that impossible to
2794
+ // get half-right.
2795
+ claimedMessageIds: jobs.map((j) => j.message_id),
2778
2796
  userMessages: userMessages.map((m) => ({
2779
2797
  id: m.id,
2780
2798
  content: m.content
2781
2799
  }))
2782
2800
  });
2783
2801
  const eventLists = await Promise.all(
2784
- completedJobs.map(
2802
+ replayableJobs.map(
2785
2803
  (job) => this.session.client.getConversationEvents(conversationId, job.job_id).catch(() => [])
2786
2804
  )
2787
2805
  );
2788
2806
  if (stopReplay()) return false;
2789
2807
  const eventsByJobId = new Map(
2790
- completedJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
2808
+ replayableJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
2791
2809
  );
2792
2810
  for (const step of plan) {
2793
2811
  if (stopReplay()) return false;
@@ -2809,11 +2827,14 @@ var StreamManager = class {
2809
2827
  );
2810
2828
  }
2811
2829
  if (stopReplay()) return false;
2812
- if (completedJobs.length > 0) {
2830
+ const versionCount = replayableJobs.filter(
2831
+ (j) => j.status === "completed"
2832
+ ).length;
2833
+ if (versionCount > 0) {
2813
2834
  this.emit({
2814
2835
  type: "versionsReady",
2815
2836
  conversationId,
2816
- count: completedJobs.length
2837
+ count: versionCount
2817
2838
  });
2818
2839
  }
2819
2840
  } catch {