@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.d.cts CHANGED
@@ -566,7 +566,7 @@ interface Conversation {
566
566
  /**
567
567
  * The project this task belongs to (`owner/repo`), or `null` for an ordinary
568
568
  * conversation. Set on the first turn and immutable after. Absent (rather than
569
- * null) from an Astralform older than 0.70.0.
569
+ * null) from an Astralform older than 0.69.46.
570
570
  */
571
571
  repository?: string | null;
572
572
  }
@@ -619,15 +619,30 @@ interface AgentInfo {
619
619
  isEnabled: boolean;
620
620
  avatarUrl?: string;
621
621
  /**
622
- * What the agent is for. A client shows Projects and Tasks only for `"code"`.
623
- * Absent on Astralform older than 0.70.0 treat that as `"chat"`, which is
624
- * also the server's default.
622
+ * Whether this agent's tasks can name a repository the workspace has GitHub
623
+ * connected and enabled here. A client shows Projects and Tasks on it.
624
+ *
625
+ * Derived by the server from the connector, so it cannot go stale the way the
626
+ * retired `mode` toggle could. It gates a SURFACE, not an ability: naming a
627
+ * repository is optional on every task, and a task that names none is an
628
+ * ordinary chat. Absent on Astralform older than 0.69.50 — fall back to `mode`
629
+ * there.
625
630
  *
626
631
  * It is a property of the WORKSPACE, not of a persona: `GET /v1/agents` selects
627
- * the workspace row itself and returns exactly one entry, so the mode is
628
- * `agents[0].mode` rather than something that varies across the list. The
629
- * workspace picker (`listAgents`) does not carry it, so a client learns an
630
- * agent's mode after opening it.
632
+ * the workspace row itself and returns exactly one entry, so read
633
+ * `agents[0].codeProjectsEnabled`. The workspace picker (`listAgents`) does not
634
+ * carry it, so a client learns this after opening an agent.
635
+ */
636
+ codeProjectsEnabled?: boolean;
637
+ /**
638
+ * @deprecated Removed in the next Astralform release. There is no agent mode —
639
+ * a repository belongs to the TASK, so one agent answers general questions and
640
+ * works in repositories from the same list. Read {@link codeProjectsEnabled}.
641
+ *
642
+ * Still reported for one release, as the STORED value of the retired column, so
643
+ * clients built before the change keep behaving exactly as they did. Do not
644
+ * treat it as an alias for `codeProjectsEnabled`: an agent that never had the
645
+ * toggle set still reports `"chat"` while its tasks can bind perfectly well.
631
646
  */
632
647
  mode?: "chat" | "code";
633
648
  }
@@ -759,12 +774,13 @@ interface ChatStreamRequest {
759
774
  /**
760
775
  * Which project (GitHub repository, `owner/repo`) this task belongs to.
761
776
  *
762
- * Required on the FIRST turn of a conversation on a code-mode agent, which
763
- * binds it. A later turn may omit it or repeat the same value; a DIFFERENT
764
- * value is refused (409), not ignored — a task is bound to one repository for
765
- * life, so a different repository means a new task. Chat-mode agents ignore
766
- * it entirely.
767
- * Astralform >= 0.70.0.
777
+ * Optional on every agent. The first turn that names one binds the task, and a
778
+ * later turn may omit it or repeat the same value; a DIFFERENT value is refused
779
+ * (409), not ignored — a task is bound to one repository for life, so a
780
+ * different repository means a new task. A task that never names one is an
781
+ * ordinary chat; since Astralform 0.69.50 there is no agent mode that requires
782
+ * one.
783
+ * Astralform >= 0.69.46.
768
784
  */
769
785
  repository?: string;
770
786
  mcp_manifest?: ToolDefinition[];
@@ -791,7 +807,7 @@ interface ChatStreamRequest {
791
807
  reasoning_effort?: ReasoningEffort;
792
808
  temperature?: number;
793
809
  }
794
- /** One repository an app user works with on a code-mode agent. */
810
+ /** One repository an app user works with on an agent. */
795
811
  interface CodeProject {
796
812
  repoFullName: string;
797
813
  addedAt: string;
@@ -1064,13 +1080,13 @@ interface SendOptions$1 extends ModelChoiceOptions {
1064
1080
  */
1065
1081
  goal?: string;
1066
1082
  /**
1067
- * The project this task belongs to (`owner/repo`), on a code-mode agent.
1083
+ * The project this task belongs to (`owner/repo`), when it has one.
1068
1084
  *
1069
1085
  * Send it on the turn that STARTS a task; the binding is write-once, so a
1070
1086
  * later turn may omit it or repeat the same value, and a DIFFERENT value is
1071
- * refused (409) rather than ignored. A first turn without it on a code-mode
1072
- * agent is refused too (400) the run needs a repository before it can hold a
1073
- * credential scoped to one. Astralform >= 0.70.0.
1087
+ * refused (409) rather than ignored. Omit it entirely and the task is an
1088
+ * ordinary chatsince Astralform 0.69.50 no agent requires one, and a first
1089
+ * turn without it is no longer a 400. Astralform >= 0.69.46.
1074
1090
  */
1075
1091
  repository?: string;
1076
1092
  }
@@ -1223,9 +1239,10 @@ declare class AstralformClient {
1223
1239
  /**
1224
1240
  * A page of conversations, newest-updated first.
1225
1241
  *
1226
- * `options.repository` narrows to one project's tasks (`owner/repo`) on a
1227
- * code-mode agent — the same paging applies within the filter, so a client
1228
- * showing tasks per project pages each project separately.
1242
+ * `options.repository` narrows to one project's tasks (`owner/repo`) the same
1243
+ * paging applies within the filter, so a client showing tasks per project pages
1244
+ * each project separately. Tasks that named no repository fall outside every
1245
+ * such filter; list them with no filter at all.
1229
1246
  */
1230
1247
  getConversations(limit?: number, offset?: number, options?: {
1231
1248
  repository?: string;
@@ -1318,10 +1335,11 @@ declare class AstralformClient {
1318
1335
  * The projects (GitHub repositories) this app user works with, and what they
1319
1336
  * may add.
1320
1337
  *
1321
- * A project list is per app user within a code-mode agent: the developer
1322
- * connects the workspace's GitHub account, and each user curates their own
1323
- * list from what that connection covers. Every method 404s on a chat-mode
1324
- * agent, so the surface is invisible rather than empty there.
1338
+ * A project list is per app user: the developer connects the workspace's GitHub
1339
+ * account, and each user curates their own list from what that connection
1340
+ * covers. There is no agent-level gate an agent with no GitHub lists nothing,
1341
+ * reports `not_installed` from `available()`, and refuses `add()`. Read
1342
+ * {@link AgentInfo.codeProjectsEnabled} to decide whether to show the surface.
1325
1343
  */
1326
1344
  readonly code: {
1327
1345
  projects: {
@@ -1820,14 +1838,15 @@ interface SendOptions extends ModelChoiceOptions {
1820
1838
  */
1821
1839
  goal?: string;
1822
1840
  /**
1823
- * The project this task belongs to (`owner/repo`), on a code-mode agent.
1841
+ * The project this task belongs to (`owner/repo`), when it has one.
1824
1842
  *
1825
1843
  * Write-once server-side, and the refusal is the part that matters: the FIRST
1826
- * turn binds the task, a later turn may omit it or repeat the same value, and
1827
- * a DIFFERENT value is refused (409) rather than ignored — silently acting on
1828
- * the wrong repository is the failure that rule exists to prevent. A first
1829
- * turn without it on a code-mode agent is refused too (400). Astralform
1830
- * >= 0.70.0.
1844
+ * turn that names a repository binds the task, a later turn may omit it or
1845
+ * repeat the same value, and a DIFFERENT value is refused (409) rather than
1846
+ * ignored — silently acting on the wrong repository is the failure that rule
1847
+ * exists to prevent. Omit it entirely and the task is an ordinary chat: since
1848
+ * Astralform 0.69.50 there is no agent mode that requires one, so a first turn
1849
+ * without it is no longer a 400. Astralform >= 0.69.46.
1831
1850
  */
1832
1851
  repository?: string;
1833
1852
  }
@@ -1999,6 +2018,14 @@ declare class StreamManager {
1999
2018
  */
2000
2019
  private settleIdle;
2001
2020
  private finalizeStream;
2021
+ /**
2022
+ * A conversation's turns, oldest first.
2023
+ *
2024
+ * Its own method so ``restore`` can put the request on the wire beside the
2025
+ * probe and the message list while ``replayHistory``, which consumes it,
2026
+ * keeps owning the shape it reads.
2027
+ */
2028
+ private jobList;
2002
2029
  private restore;
2003
2030
  /**
2004
2031
  * Has a live turn taken the block view over?
@@ -2037,6 +2064,12 @@ declare class StreamManager {
2037
2064
  * that started it, which is emitted as a bubble with no events — the whole
2038
2065
  * reason a conversation reopened mid-turn now shows the message that started
2039
2066
  * that turn.
2067
+ *
2068
+ * ``jobsRequest`` is the job list already IN FLIGHT — issued by ``restore``
2069
+ * alongside the probe and the message list rather than fetched here, so the
2070
+ * three round trips overlap. It is awaited inside the try below, which is
2071
+ * what keeps a failed job list non-blocking exactly as it was when the fetch
2072
+ * lived here.
2040
2073
  */
2041
2074
  private replayHistory;
2042
2075
  private setActiveConversation;
package/dist/index.d.ts CHANGED
@@ -566,7 +566,7 @@ interface Conversation {
566
566
  /**
567
567
  * The project this task belongs to (`owner/repo`), or `null` for an ordinary
568
568
  * conversation. Set on the first turn and immutable after. Absent (rather than
569
- * null) from an Astralform older than 0.70.0.
569
+ * null) from an Astralform older than 0.69.46.
570
570
  */
571
571
  repository?: string | null;
572
572
  }
@@ -619,15 +619,30 @@ interface AgentInfo {
619
619
  isEnabled: boolean;
620
620
  avatarUrl?: string;
621
621
  /**
622
- * What the agent is for. A client shows Projects and Tasks only for `"code"`.
623
- * Absent on Astralform older than 0.70.0 treat that as `"chat"`, which is
624
- * also the server's default.
622
+ * Whether this agent's tasks can name a repository the workspace has GitHub
623
+ * connected and enabled here. A client shows Projects and Tasks on it.
624
+ *
625
+ * Derived by the server from the connector, so it cannot go stale the way the
626
+ * retired `mode` toggle could. It gates a SURFACE, not an ability: naming a
627
+ * repository is optional on every task, and a task that names none is an
628
+ * ordinary chat. Absent on Astralform older than 0.69.50 — fall back to `mode`
629
+ * there.
625
630
  *
626
631
  * It is a property of the WORKSPACE, not of a persona: `GET /v1/agents` selects
627
- * the workspace row itself and returns exactly one entry, so the mode is
628
- * `agents[0].mode` rather than something that varies across the list. The
629
- * workspace picker (`listAgents`) does not carry it, so a client learns an
630
- * agent's mode after opening it.
632
+ * the workspace row itself and returns exactly one entry, so read
633
+ * `agents[0].codeProjectsEnabled`. The workspace picker (`listAgents`) does not
634
+ * carry it, so a client learns this after opening an agent.
635
+ */
636
+ codeProjectsEnabled?: boolean;
637
+ /**
638
+ * @deprecated Removed in the next Astralform release. There is no agent mode —
639
+ * a repository belongs to the TASK, so one agent answers general questions and
640
+ * works in repositories from the same list. Read {@link codeProjectsEnabled}.
641
+ *
642
+ * Still reported for one release, as the STORED value of the retired column, so
643
+ * clients built before the change keep behaving exactly as they did. Do not
644
+ * treat it as an alias for `codeProjectsEnabled`: an agent that never had the
645
+ * toggle set still reports `"chat"` while its tasks can bind perfectly well.
631
646
  */
632
647
  mode?: "chat" | "code";
633
648
  }
@@ -759,12 +774,13 @@ interface ChatStreamRequest {
759
774
  /**
760
775
  * Which project (GitHub repository, `owner/repo`) this task belongs to.
761
776
  *
762
- * Required on the FIRST turn of a conversation on a code-mode agent, which
763
- * binds it. A later turn may omit it or repeat the same value; a DIFFERENT
764
- * value is refused (409), not ignored — a task is bound to one repository for
765
- * life, so a different repository means a new task. Chat-mode agents ignore
766
- * it entirely.
767
- * Astralform >= 0.70.0.
777
+ * Optional on every agent. The first turn that names one binds the task, and a
778
+ * later turn may omit it or repeat the same value; a DIFFERENT value is refused
779
+ * (409), not ignored — a task is bound to one repository for life, so a
780
+ * different repository means a new task. A task that never names one is an
781
+ * ordinary chat; since Astralform 0.69.50 there is no agent mode that requires
782
+ * one.
783
+ * Astralform >= 0.69.46.
768
784
  */
769
785
  repository?: string;
770
786
  mcp_manifest?: ToolDefinition[];
@@ -791,7 +807,7 @@ interface ChatStreamRequest {
791
807
  reasoning_effort?: ReasoningEffort;
792
808
  temperature?: number;
793
809
  }
794
- /** One repository an app user works with on a code-mode agent. */
810
+ /** One repository an app user works with on an agent. */
795
811
  interface CodeProject {
796
812
  repoFullName: string;
797
813
  addedAt: string;
@@ -1064,13 +1080,13 @@ interface SendOptions$1 extends ModelChoiceOptions {
1064
1080
  */
1065
1081
  goal?: string;
1066
1082
  /**
1067
- * The project this task belongs to (`owner/repo`), on a code-mode agent.
1083
+ * The project this task belongs to (`owner/repo`), when it has one.
1068
1084
  *
1069
1085
  * Send it on the turn that STARTS a task; the binding is write-once, so a
1070
1086
  * later turn may omit it or repeat the same value, and a DIFFERENT value is
1071
- * refused (409) rather than ignored. A first turn without it on a code-mode
1072
- * agent is refused too (400) the run needs a repository before it can hold a
1073
- * credential scoped to one. Astralform >= 0.70.0.
1087
+ * refused (409) rather than ignored. Omit it entirely and the task is an
1088
+ * ordinary chatsince Astralform 0.69.50 no agent requires one, and a first
1089
+ * turn without it is no longer a 400. Astralform >= 0.69.46.
1074
1090
  */
1075
1091
  repository?: string;
1076
1092
  }
@@ -1223,9 +1239,10 @@ declare class AstralformClient {
1223
1239
  /**
1224
1240
  * A page of conversations, newest-updated first.
1225
1241
  *
1226
- * `options.repository` narrows to one project's tasks (`owner/repo`) on a
1227
- * code-mode agent — the same paging applies within the filter, so a client
1228
- * showing tasks per project pages each project separately.
1242
+ * `options.repository` narrows to one project's tasks (`owner/repo`) the same
1243
+ * paging applies within the filter, so a client showing tasks per project pages
1244
+ * each project separately. Tasks that named no repository fall outside every
1245
+ * such filter; list them with no filter at all.
1229
1246
  */
1230
1247
  getConversations(limit?: number, offset?: number, options?: {
1231
1248
  repository?: string;
@@ -1318,10 +1335,11 @@ declare class AstralformClient {
1318
1335
  * The projects (GitHub repositories) this app user works with, and what they
1319
1336
  * may add.
1320
1337
  *
1321
- * A project list is per app user within a code-mode agent: the developer
1322
- * connects the workspace's GitHub account, and each user curates their own
1323
- * list from what that connection covers. Every method 404s on a chat-mode
1324
- * agent, so the surface is invisible rather than empty there.
1338
+ * A project list is per app user: the developer connects the workspace's GitHub
1339
+ * account, and each user curates their own list from what that connection
1340
+ * covers. There is no agent-level gate an agent with no GitHub lists nothing,
1341
+ * reports `not_installed` from `available()`, and refuses `add()`. Read
1342
+ * {@link AgentInfo.codeProjectsEnabled} to decide whether to show the surface.
1325
1343
  */
1326
1344
  readonly code: {
1327
1345
  projects: {
@@ -1820,14 +1838,15 @@ interface SendOptions extends ModelChoiceOptions {
1820
1838
  */
1821
1839
  goal?: string;
1822
1840
  /**
1823
- * The project this task belongs to (`owner/repo`), on a code-mode agent.
1841
+ * The project this task belongs to (`owner/repo`), when it has one.
1824
1842
  *
1825
1843
  * Write-once server-side, and the refusal is the part that matters: the FIRST
1826
- * turn binds the task, a later turn may omit it or repeat the same value, and
1827
- * a DIFFERENT value is refused (409) rather than ignored — silently acting on
1828
- * the wrong repository is the failure that rule exists to prevent. A first
1829
- * turn without it on a code-mode agent is refused too (400). Astralform
1830
- * >= 0.70.0.
1844
+ * turn that names a repository binds the task, a later turn may omit it or
1845
+ * repeat the same value, and a DIFFERENT value is refused (409) rather than
1846
+ * ignored — silently acting on the wrong repository is the failure that rule
1847
+ * exists to prevent. Omit it entirely and the task is an ordinary chat: since
1848
+ * Astralform 0.69.50 there is no agent mode that requires one, so a first turn
1849
+ * without it is no longer a 400. Astralform >= 0.69.46.
1831
1850
  */
1832
1851
  repository?: string;
1833
1852
  }
@@ -1999,6 +2018,14 @@ declare class StreamManager {
1999
2018
  */
2000
2019
  private settleIdle;
2001
2020
  private finalizeStream;
2021
+ /**
2022
+ * A conversation's turns, oldest first.
2023
+ *
2024
+ * Its own method so ``restore`` can put the request on the wire beside the
2025
+ * probe and the message list while ``replayHistory``, which consumes it,
2026
+ * keeps owning the shape it reads.
2027
+ */
2028
+ private jobList;
2002
2029
  private restore;
2003
2030
  /**
2004
2031
  * Has a live turn taken the block view over?
@@ -2037,6 +2064,12 @@ declare class StreamManager {
2037
2064
  * that started it, which is emitted as a bubble with no events — the whole
2038
2065
  * reason a conversation reopened mid-turn now shows the message that started
2039
2066
  * that turn.
2067
+ *
2068
+ * ``jobsRequest`` is the job list already IN FLIGHT — issued by ``restore``
2069
+ * alongside the probe and the message list rather than fetched here, so the
2070
+ * three round trips overlap. It is awaited inside the try below, which is
2071
+ * what keeps a failed job list non-blocking exactly as it was when the fetch
2072
+ * lived here.
2040
2073
  */
2041
2074
  private replayHistory;
2042
2075
  private setActiveConversation;
package/dist/index.js CHANGED
@@ -338,15 +338,16 @@ function isApiKeyConfig(config) {
338
338
  }
339
339
  var AstralformClient = class {
340
340
  constructor(config) {
341
- // --- Code mode: the app user's projects ---
341
+ // --- Projects: the repositories this app user works with ---
342
342
  /**
343
343
  * The projects (GitHub repositories) this app user works with, and what they
344
344
  * may add.
345
345
  *
346
- * A project list is per app user within a code-mode agent: the developer
347
- * connects the workspace's GitHub account, and each user curates their own
348
- * list from what that connection covers. Every method 404s on a chat-mode
349
- * agent, so the surface is invisible rather than empty there.
346
+ * A project list is per app user: the developer connects the workspace's GitHub
347
+ * account, and each user curates their own list from what that connection
348
+ * covers. There is no agent-level gate an agent with no GitHub lists nothing,
349
+ * reports `not_installed` from `available()`, and refuses `add()`. Read
350
+ * {@link AgentInfo.codeProjectsEnabled} to decide whether to show the surface.
350
351
  */
351
352
  this.code = {
352
353
  projects: {
@@ -644,9 +645,10 @@ var AstralformClient = class {
644
645
  /**
645
646
  * A page of conversations, newest-updated first.
646
647
  *
647
- * `options.repository` narrows to one project's tasks (`owner/repo`) on a
648
- * code-mode agent — the same paging applies within the filter, so a client
649
- * showing tasks per project pages each project separately.
648
+ * `options.repository` narrows to one project's tasks (`owner/repo`) the same
649
+ * paging applies within the filter, so a client showing tasks per project pages
650
+ * each project separately. Tasks that named no repository fall outside every
651
+ * such filter; list them with no filter at all.
650
652
  */
651
653
  async getConversations(limit = 50, offset = 0, options) {
652
654
  const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));
@@ -1747,7 +1749,7 @@ var ChatSession = class {
1747
1749
  image_mode: options?.imageMode,
1748
1750
  video_mode: options?.videoMode,
1749
1751
  goal: options?.goal,
1750
- // The project this task belongs to, on a code-mode agent. Write-once
1752
+ // The project this task belongs to, when it has one. Write-once
1751
1753
  // server-side: sent on every turn, honoured on the first.
1752
1754
  repository: options?.repository,
1753
1755
  // Per-request model choice (client-side model selection).
@@ -2885,23 +2887,41 @@ var StreamManager = class {
2885
2887
  }
2886
2888
  }
2887
2889
  // ── Internal: restore ─────────────────────────────────────────
2890
+ /**
2891
+ * A conversation's turns, oldest first.
2892
+ *
2893
+ * Its own method so ``restore`` can put the request on the wire beside the
2894
+ * probe and the message list while ``replayHistory``, which consumes it,
2895
+ * keeps owning the shape it reads.
2896
+ */
2897
+ jobList(conversationId) {
2898
+ return this.session.client.get(
2899
+ `/v1/conversations/${encodeURIComponent(conversationId)}/jobs`
2900
+ );
2901
+ }
2888
2902
  async restore(conversationId, gen) {
2889
2903
  const superseded = () => gen !== this.generation;
2890
2904
  if (superseded()) return;
2891
2905
  const turn = this.turnCounter;
2892
2906
  const announcedRestoring = !this.session.isStreaming;
2893
2907
  if (announcedRestoring) this.setState("restoring");
2894
- let activeJobId = null;
2895
- try {
2896
- const res = await this.session.client.getActiveJob(conversationId);
2897
- activeJobId = res.jobId;
2898
- } catch {
2899
- }
2900
2908
  if (superseded()) return;
2901
- await this.session.loadConversation(conversationId);
2909
+ const probeRequest = this.session.client.getActiveJob(conversationId).catch(() => null);
2910
+ const loadRequest = this.session.loadConversation(conversationId);
2911
+ const jobsRequest = announcedRestoring ? this.jobList(conversationId) : null;
2912
+ void jobsRequest?.catch(() => {
2913
+ });
2914
+ const [probe] = await Promise.all([probeRequest, loadRequest]);
2915
+ const activeJobId = probe?.jobId ?? null;
2902
2916
  if (superseded()) return;
2903
2917
  if (announcedRestoring && this.viewTakenOverByLiveTurn()) return;
2904
- if (announcedRestoring && !await this.replayHistory(conversationId, gen, activeJobId, turn))
2918
+ if (jobsRequest && !await this.replayHistory(
2919
+ conversationId,
2920
+ gen,
2921
+ activeJobId,
2922
+ turn,
2923
+ jobsRequest
2924
+ ))
2905
2925
  return;
2906
2926
  if (activeJobId) {
2907
2927
  this.setState("streaming");
@@ -2959,11 +2979,17 @@ var StreamManager = class {
2959
2979
  * that started it, which is emitted as a bubble with no events — the whole
2960
2980
  * reason a conversation reopened mid-turn now shows the message that started
2961
2981
  * that turn.
2982
+ *
2983
+ * ``jobsRequest`` is the job list already IN FLIGHT — issued by ``restore``
2984
+ * alongside the probe and the message list rather than fetched here, so the
2985
+ * three round trips overlap. It is awaited inside the try below, which is
2986
+ * what keeps a failed job list non-blocking exactly as it was when the fetch
2987
+ * lived here.
2962
2988
  */
2963
- async replayHistory(conversationId, gen, activeJobId, turn) {
2989
+ async replayHistory(conversationId, gen, activeJobId, turn, jobsRequest) {
2964
2990
  const stopReplay = () => gen !== this.generation || this.viewTakenOverByLiveTurn() || this.turnStarted(turn);
2965
2991
  try {
2966
- const jobs = await this.session.client.get(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);
2992
+ const jobs = await jobsRequest;
2967
2993
  if (stopReplay()) return false;
2968
2994
  const replayableJobs = jobs.filter(
2969
2995
  (j) => j.job_id !== activeJobId