@astralform/js 7.4.1 → 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
@@ -389,15 +389,16 @@ function isApiKeyConfig(config) {
389
389
  }
390
390
  var AstralformClient = class {
391
391
  constructor(config) {
392
- // --- Code mode: the app user's projects ---
392
+ // --- Projects: the repositories this app user works with ---
393
393
  /**
394
394
  * The projects (GitHub repositories) this app user works with, and what they
395
395
  * may add.
396
396
  *
397
- * A project list is per app user within a code-mode agent: the developer
398
- * connects the workspace's GitHub account, and each user curates their own
399
- * list from what that connection covers. Every method 404s on a chat-mode
400
- * agent, so the surface is invisible rather than empty there.
397
+ * A project list is per app user: the developer connects the workspace's GitHub
398
+ * account, and each user curates their own list from what that connection
399
+ * covers. There is no agent-level gate an agent with no GitHub lists nothing,
400
+ * reports `not_installed` from `available()`, and refuses `add()`. Read
401
+ * {@link AgentInfo.codeProjectsEnabled} to decide whether to show the surface.
401
402
  */
402
403
  this.code = {
403
404
  projects: {
@@ -695,9 +696,10 @@ var AstralformClient = class {
695
696
  /**
696
697
  * A page of conversations, newest-updated first.
697
698
  *
698
- * `options.repository` narrows to one project's tasks (`owner/repo`) on a
699
- * code-mode agent — the same paging applies within the filter, so a client
700
- * showing tasks per project pages each project separately.
699
+ * `options.repository` narrows to one project's tasks (`owner/repo`) the same
700
+ * paging applies within the filter, so a client showing tasks per project pages
701
+ * each project separately. Tasks that named no repository fall outside every
702
+ * such filter; list them with no filter at all.
701
703
  */
702
704
  async getConversations(limit = 50, offset = 0, options) {
703
705
  const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));
@@ -1798,7 +1800,7 @@ var ChatSession = class {
1798
1800
  image_mode: options?.imageMode,
1799
1801
  video_mode: options?.videoMode,
1800
1802
  goal: options?.goal,
1801
- // The project this task belongs to, on a code-mode agent. Write-once
1803
+ // The project this task belongs to, when it has one. Write-once
1802
1804
  // server-side: sent on every turn, honoured on the first.
1803
1805
  repository: options?.repository,
1804
1806
  // Per-request model choice (client-side model selection).
@@ -2936,23 +2938,41 @@ var StreamManager = class {
2936
2938
  }
2937
2939
  }
2938
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
+ }
2939
2953
  async restore(conversationId, gen) {
2940
2954
  const superseded = () => gen !== this.generation;
2941
2955
  if (superseded()) return;
2942
2956
  const turn = this.turnCounter;
2943
2957
  const announcedRestoring = !this.session.isStreaming;
2944
2958
  if (announcedRestoring) this.setState("restoring");
2945
- let activeJobId = null;
2946
- try {
2947
- const res = await this.session.client.getActiveJob(conversationId);
2948
- activeJobId = res.jobId;
2949
- } catch {
2950
- }
2951
2959
  if (superseded()) return;
2952
- 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;
2953
2967
  if (superseded()) return;
2954
2968
  if (announcedRestoring && this.viewTakenOverByLiveTurn()) return;
2955
- 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
+ ))
2956
2976
  return;
2957
2977
  if (activeJobId) {
2958
2978
  this.setState("streaming");
@@ -3010,11 +3030,17 @@ var StreamManager = class {
3010
3030
  * that started it, which is emitted as a bubble with no events — the whole
3011
3031
  * reason a conversation reopened mid-turn now shows the message that started
3012
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.
3013
3039
  */
3014
- async replayHistory(conversationId, gen, activeJobId, turn) {
3040
+ async replayHistory(conversationId, gen, activeJobId, turn, jobsRequest) {
3015
3041
  const stopReplay = () => gen !== this.generation || this.viewTakenOverByLiveTurn() || this.turnStarted(turn);
3016
3042
  try {
3017
- const jobs = await this.session.client.get(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);
3043
+ const jobs = await jobsRequest;
3018
3044
  if (stopReplay()) return false;
3019
3045
  const replayableJobs = jobs.filter(
3020
3046
  (j) => j.job_id !== activeJobId