@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.js CHANGED
@@ -2212,6 +2212,13 @@ function planRestore(args) {
2212
2212
  content: msgs[i]?.content,
2213
2213
  messageId: msgs[i]?.id
2214
2214
  }));
2215
+ if (jobs.length === 0) {
2216
+ return userMessages.map((m) => ({
2217
+ kind: "steer",
2218
+ content: m.content,
2219
+ messageId: m.id
2220
+ }));
2221
+ }
2215
2222
  const firstLinked = jobs.findIndex((j) => linkOf(j) !== void 0);
2216
2223
  if (firstLinked === -1) {
2217
2224
  return positional(jobs, userMessages);
@@ -2704,15 +2711,15 @@ var StreamManager = class {
2704
2711
  try {
2705
2712
  const jobs = await this.session.client.get(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);
2706
2713
  if (stopReplay()) return false;
2707
- const completedJobs = jobs.filter(
2708
- (j) => j.status === "completed" && j.job_id !== activeJobId
2714
+ const replayableJobs = jobs.filter(
2715
+ (j) => j.job_id !== activeJobId
2709
2716
  );
2710
2717
  const userMessages = this.session.messages.filter(
2711
2718
  (m) => m.role === "user"
2712
2719
  );
2713
2720
  const runningJob = activeJobId ? jobs.find((j) => j.job_id === activeJobId) : void 0;
2714
2721
  const plan = planRestore({
2715
- completedJobs: completedJobs.map((j) => ({
2722
+ completedJobs: replayableJobs.map((j) => ({
2716
2723
  job_id: j.job_id,
2717
2724
  message_id: j.message_id
2718
2725
  })),
@@ -2720,27 +2727,38 @@ var StreamManager = class {
2720
2727
  job_id: runningJob.job_id,
2721
2728
  message_id: runningJob.message_id
2722
2729
  },
2723
- // Completed jobs PLUS the ones still going. A send landing in the
2724
- // probe window has a running job, so its prompt is claimed and does
2725
- // not read as a steer replayed over the bubble the live send already
2726
- // rendered. Failed and cancelled jobs are deliberately NOT claimed:
2727
- // they produce no `turn` step, so claiming them would delete the
2728
- // user's prompt from the restore entirely rather than show it as a
2729
- // steer.
2730
- claimedMessageIds: jobs.filter((j) => j.status !== "failed" && j.status !== "cancelled").map((j) => j.message_id),
2730
+ // EVERY job, which is the same set the replay walk gets. The claim set
2731
+ // answers one question "will some turn step already draw this prompt?"
2732
+ // so it is a RESTATEMENT of the walk, and the two drifting apart is
2733
+ // what produces either a missing bubble or a doubled one.
2734
+ //
2735
+ // Failed and cancelled were excluded here for exactly one reason: they
2736
+ // produced no turn step, which is the bug fixed above. Now that they do,
2737
+ // the exclusion has no case left to describe.
2738
+ //
2739
+ // Being precise about what this change does and does not do: it is not
2740
+ // load-bearing TODAY. `planRestore` anchors a prompt at the index its
2741
+ // job links to and advances the cursor past it, so a message claimed by
2742
+ // a job inside the walk is never offered to the steer branch anyway —
2743
+ // the two spellings agree on current inputs. It is here because the
2744
+ // invariant is what keeps them agreeing: narrow the walk again without
2745
+ // narrowing this, and the prompts of the jobs dropped from it go with
2746
+ // them, silently. Deriving both from `jobs` makes that impossible to
2747
+ // get half-right.
2748
+ claimedMessageIds: jobs.map((j) => j.message_id),
2731
2749
  userMessages: userMessages.map((m) => ({
2732
2750
  id: m.id,
2733
2751
  content: m.content
2734
2752
  }))
2735
2753
  });
2736
2754
  const eventLists = await Promise.all(
2737
- completedJobs.map(
2755
+ replayableJobs.map(
2738
2756
  (job) => this.session.client.getConversationEvents(conversationId, job.job_id).catch(() => [])
2739
2757
  )
2740
2758
  );
2741
2759
  if (stopReplay()) return false;
2742
2760
  const eventsByJobId = new Map(
2743
- completedJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
2761
+ replayableJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
2744
2762
  );
2745
2763
  for (const step of plan) {
2746
2764
  if (stopReplay()) return false;
@@ -2762,11 +2780,14 @@ var StreamManager = class {
2762
2780
  );
2763
2781
  }
2764
2782
  if (stopReplay()) return false;
2765
- if (completedJobs.length > 0) {
2783
+ const versionCount = replayableJobs.filter(
2784
+ (j) => j.status === "completed"
2785
+ ).length;
2786
+ if (versionCount > 0) {
2766
2787
  this.emit({
2767
2788
  type: "versionsReady",
2768
2789
  conversationId,
2769
- count: completedJobs.length
2790
+ count: versionCount
2770
2791
  });
2771
2792
  }
2772
2793
  } catch {