@astralform/js 3.0.0 → 3.1.0

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
@@ -615,6 +615,19 @@ interface FeedbackResponse {
615
615
  comment: string | null;
616
616
  createdAt: string;
617
617
  }
618
+ /** Reasoning effort a caller may request for a turn (client-side model selection). */
619
+ type ReasoningEffort = "low" | "medium" | "high";
620
+ /**
621
+ * The per-request model choice (client-side model selection). `provider` and
622
+ * `model` are paired — send both or neither; when omitted, the server reuses the
623
+ * conversation's last model or a connected-provider default.
624
+ */
625
+ interface ModelChoiceOptions {
626
+ provider?: string;
627
+ model?: string;
628
+ reasoningEffort?: ReasoningEffort;
629
+ temperature?: number;
630
+ }
618
631
  interface ChatStreamRequest {
619
632
  message?: string;
620
633
  conversation_id?: string;
@@ -626,6 +639,14 @@ interface ChatStreamRequest {
626
639
  agent_name?: string;
627
640
  enable_search?: boolean;
628
641
  plan_mode?: boolean;
642
+ /**
643
+ * Per-request model choice (client-side model selection), wire shape.
644
+ * `provider` and `model` are paired; omit to reuse the thread's last model.
645
+ */
646
+ provider?: string;
647
+ model?: string;
648
+ reasoning_effort?: ReasoningEffort;
649
+ temperature?: number;
629
650
  }
630
651
  interface ToolResultRequest {
631
652
  conversation_id: string;
@@ -697,7 +718,7 @@ interface ConversationEvent {
697
718
  event: string;
698
719
  data: Record<string, unknown>;
699
720
  }
700
- interface SendOptions$1 {
721
+ interface SendOptions$1 extends ModelChoiceOptions {
701
722
  conversationId?: string;
702
723
  enabledClientTools?: string[];
703
724
  uploadIds?: string[];
@@ -705,6 +726,19 @@ interface SendOptions$1 {
705
726
  enableSearch?: boolean;
706
727
  planMode?: boolean;
707
728
  }
729
+ /**
730
+ * A selectable model for one of the team's connected providers, from
731
+ * `GET /v1/models`. Backs the client-side model picker.
732
+ */
733
+ interface ModelOption {
734
+ provider: string;
735
+ providerDisplay: string;
736
+ model: string;
737
+ thinking: boolean;
738
+ tools: boolean;
739
+ vision: boolean;
740
+ thinkingMode: string;
741
+ }
708
742
  interface ConversationAsset {
709
743
  id: string;
710
744
  kind: "upload" | "output";
@@ -787,6 +821,13 @@ declare class AstralformClient {
787
821
  * which enumerates the team-level agents a signed-in user can open.
788
822
  */
789
823
  getAgents(): Promise<AgentInfo[]>;
824
+ /**
825
+ * List the models the caller may pick this turn — expanded from the curated
826
+ * catalog of the providers the team has connected (client-side model
827
+ * selection). Backs the composer's model picker. Scoped to the active agent
828
+ * via X-Agent-ID, same as {@link getAgentStatus}.
829
+ */
830
+ getModels(): Promise<ModelOption[]>;
790
831
  getSkills(): Promise<SkillInfo[]>;
791
832
  getConversationEvents(conversationId: string, jobId?: string): Promise<ConversationEvent[]>;
792
833
  submitToolResult(request: ToolResultRequest): Promise<void>;
@@ -988,10 +1029,11 @@ declare class ChatSession {
988
1029
  switchConversation(id: string, jobId?: string,
989
1030
  /**
990
1031
  * User prompt that triggered this job, if known. Emitted as a
991
- * synthetic ``user_message`` ChatEvent right before the first
992
- * ``message_start`` of the replay. User messages aren't persisted
993
- * in ``job_events``, so without this the restored conversation
994
- * would show the agent response with no visible prompt above it.
1032
+ * synthetic ``user_message`` ChatEvent at the START of the replay —
1033
+ * a completed job maps to exactly one user turn, so every event in it
1034
+ * belongs beneath that prompt. User messages aren't persisted in
1035
+ * ``job_events``, so without this the restored conversation would show
1036
+ * the agent response with no visible prompt above it.
995
1037
  */
996
1038
  userMessageContent?: string): Promise<void>;
997
1039
  deleteConversation(id: string): Promise<void>;
@@ -1065,7 +1107,7 @@ declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<Chat
1065
1107
  */
1066
1108
 
1067
1109
  type StreamState = "idle" | "streaming" | "restoring" | "detached";
1068
- interface SendOptions {
1110
+ interface SendOptions extends ModelChoiceOptions {
1069
1111
  enableSearch?: boolean;
1070
1112
  agentName?: string;
1071
1113
  uploadIds?: string[];
@@ -1145,8 +1187,18 @@ interface RawSseEvent {
1145
1187
  declare function mapSseToChat(raw: RawSseEvent): ChatEvent[];
1146
1188
  /**
1147
1189
  * Replay persisted SSE events through the provided handler, interleaving
1148
- * user messages from session.messages at the first message_start of each
1149
- * turn (user messages aren't persisted in job_events).
1190
+ * user messages from session.messages at the START of each turn (user
1191
+ * messages aren't persisted in job_events).
1192
+ *
1193
+ * The turn boundary is a change of ``job_id``, NOT ``message_start`` or
1194
+ * ``message_stop``. A completed job maps to exactly one user turn — but a
1195
+ * single job can contain several ``message_start``/``message_stop`` pairs (a
1196
+ * tool-use loop: LLM call → tool result → LLM call again), so neither event
1197
+ * reliably delimits turns. And within a job some events precede the first
1198
+ * ``message_start`` (e.g. ``memory_recall``, emitted during prompt prep), so
1199
+ * gating the user block on ``message_start`` would replay them above the
1200
+ * user's own message. Keying off ``job_id`` injects the prompt once per job,
1201
+ * before its first event — matching ``session.ts#switchConversation``.
1150
1202
  */
1151
1203
  declare function replayEvents(sseEvents: RawSseEvent[], userMessages: {
1152
1204
  role: string;
@@ -1191,4 +1243,4 @@ declare function isEmbeddedResource(value: unknown): value is {
1191
1243
  */
1192
1244
  declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
1193
1245
 
1194
- export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
1246
+ export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
package/dist/index.d.ts CHANGED
@@ -615,6 +615,19 @@ interface FeedbackResponse {
615
615
  comment: string | null;
616
616
  createdAt: string;
617
617
  }
618
+ /** Reasoning effort a caller may request for a turn (client-side model selection). */
619
+ type ReasoningEffort = "low" | "medium" | "high";
620
+ /**
621
+ * The per-request model choice (client-side model selection). `provider` and
622
+ * `model` are paired — send both or neither; when omitted, the server reuses the
623
+ * conversation's last model or a connected-provider default.
624
+ */
625
+ interface ModelChoiceOptions {
626
+ provider?: string;
627
+ model?: string;
628
+ reasoningEffort?: ReasoningEffort;
629
+ temperature?: number;
630
+ }
618
631
  interface ChatStreamRequest {
619
632
  message?: string;
620
633
  conversation_id?: string;
@@ -626,6 +639,14 @@ interface ChatStreamRequest {
626
639
  agent_name?: string;
627
640
  enable_search?: boolean;
628
641
  plan_mode?: boolean;
642
+ /**
643
+ * Per-request model choice (client-side model selection), wire shape.
644
+ * `provider` and `model` are paired; omit to reuse the thread's last model.
645
+ */
646
+ provider?: string;
647
+ model?: string;
648
+ reasoning_effort?: ReasoningEffort;
649
+ temperature?: number;
629
650
  }
630
651
  interface ToolResultRequest {
631
652
  conversation_id: string;
@@ -697,7 +718,7 @@ interface ConversationEvent {
697
718
  event: string;
698
719
  data: Record<string, unknown>;
699
720
  }
700
- interface SendOptions$1 {
721
+ interface SendOptions$1 extends ModelChoiceOptions {
701
722
  conversationId?: string;
702
723
  enabledClientTools?: string[];
703
724
  uploadIds?: string[];
@@ -705,6 +726,19 @@ interface SendOptions$1 {
705
726
  enableSearch?: boolean;
706
727
  planMode?: boolean;
707
728
  }
729
+ /**
730
+ * A selectable model for one of the team's connected providers, from
731
+ * `GET /v1/models`. Backs the client-side model picker.
732
+ */
733
+ interface ModelOption {
734
+ provider: string;
735
+ providerDisplay: string;
736
+ model: string;
737
+ thinking: boolean;
738
+ tools: boolean;
739
+ vision: boolean;
740
+ thinkingMode: string;
741
+ }
708
742
  interface ConversationAsset {
709
743
  id: string;
710
744
  kind: "upload" | "output";
@@ -787,6 +821,13 @@ declare class AstralformClient {
787
821
  * which enumerates the team-level agents a signed-in user can open.
788
822
  */
789
823
  getAgents(): Promise<AgentInfo[]>;
824
+ /**
825
+ * List the models the caller may pick this turn — expanded from the curated
826
+ * catalog of the providers the team has connected (client-side model
827
+ * selection). Backs the composer's model picker. Scoped to the active agent
828
+ * via X-Agent-ID, same as {@link getAgentStatus}.
829
+ */
830
+ getModels(): Promise<ModelOption[]>;
790
831
  getSkills(): Promise<SkillInfo[]>;
791
832
  getConversationEvents(conversationId: string, jobId?: string): Promise<ConversationEvent[]>;
792
833
  submitToolResult(request: ToolResultRequest): Promise<void>;
@@ -988,10 +1029,11 @@ declare class ChatSession {
988
1029
  switchConversation(id: string, jobId?: string,
989
1030
  /**
990
1031
  * User prompt that triggered this job, if known. Emitted as a
991
- * synthetic ``user_message`` ChatEvent right before the first
992
- * ``message_start`` of the replay. User messages aren't persisted
993
- * in ``job_events``, so without this the restored conversation
994
- * would show the agent response with no visible prompt above it.
1032
+ * synthetic ``user_message`` ChatEvent at the START of the replay —
1033
+ * a completed job maps to exactly one user turn, so every event in it
1034
+ * belongs beneath that prompt. User messages aren't persisted in
1035
+ * ``job_events``, so without this the restored conversation would show
1036
+ * the agent response with no visible prompt above it.
995
1037
  */
996
1038
  userMessageContent?: string): Promise<void>;
997
1039
  deleteConversation(id: string): Promise<void>;
@@ -1065,7 +1107,7 @@ declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<Chat
1065
1107
  */
1066
1108
 
1067
1109
  type StreamState = "idle" | "streaming" | "restoring" | "detached";
1068
- interface SendOptions {
1110
+ interface SendOptions extends ModelChoiceOptions {
1069
1111
  enableSearch?: boolean;
1070
1112
  agentName?: string;
1071
1113
  uploadIds?: string[];
@@ -1145,8 +1187,18 @@ interface RawSseEvent {
1145
1187
  declare function mapSseToChat(raw: RawSseEvent): ChatEvent[];
1146
1188
  /**
1147
1189
  * Replay persisted SSE events through the provided handler, interleaving
1148
- * user messages from session.messages at the first message_start of each
1149
- * turn (user messages aren't persisted in job_events).
1190
+ * user messages from session.messages at the START of each turn (user
1191
+ * messages aren't persisted in job_events).
1192
+ *
1193
+ * The turn boundary is a change of ``job_id``, NOT ``message_start`` or
1194
+ * ``message_stop``. A completed job maps to exactly one user turn — but a
1195
+ * single job can contain several ``message_start``/``message_stop`` pairs (a
1196
+ * tool-use loop: LLM call → tool result → LLM call again), so neither event
1197
+ * reliably delimits turns. And within a job some events precede the first
1198
+ * ``message_start`` (e.g. ``memory_recall``, emitted during prompt prep), so
1199
+ * gating the user block on ``message_start`` would replay them above the
1200
+ * user's own message. Keying off ``job_id`` injects the prompt once per job,
1201
+ * before its first event — matching ``session.ts#switchConversation``.
1150
1202
  */
1151
1203
  declare function replayEvents(sseEvents: RawSseEvent[], userMessages: {
1152
1204
  role: string;
@@ -1191,4 +1243,4 @@ declare function isEmbeddedResource(value: unknown): value is {
1191
1243
  */
1192
1244
  declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
1193
1245
 
1194
- export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
1246
+ export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
package/dist/index.js CHANGED
@@ -495,6 +495,24 @@ var AstralformClient = class {
495
495
  avatarUrl: a.avatar_url
496
496
  }));
497
497
  }
498
+ /**
499
+ * List the models the caller may pick this turn — expanded from the curated
500
+ * catalog of the providers the team has connected (client-side model
501
+ * selection). Backs the composer's model picker. Scoped to the active agent
502
+ * via X-Agent-ID, same as {@link getAgentStatus}.
503
+ */
504
+ async getModels() {
505
+ const raw = await this.get("/v1/models");
506
+ return raw.map((m) => ({
507
+ provider: m.provider,
508
+ providerDisplay: m.provider_display,
509
+ model: m.model,
510
+ thinking: m.thinking,
511
+ tools: m.tools,
512
+ vision: m.vision,
513
+ thinkingMode: m.thinking_mode
514
+ }));
515
+ }
498
516
  async getSkills() {
499
517
  const raw = await this.get("/v1/skills");
500
518
  return raw.map((s) => ({
@@ -1246,6 +1264,11 @@ var ChatSession = class {
1246
1264
  this.emit({ type: "connected" });
1247
1265
  }
1248
1266
  async send(content, options) {
1267
+ if (options?.provider == null !== (options?.model == null)) {
1268
+ throw new Error(
1269
+ "`provider` and `model` must be supplied together (client-side model selection)."
1270
+ );
1271
+ }
1249
1272
  if (this.isStreaming) return;
1250
1273
  const conversationId = options?.conversationId ?? this.conversationId ?? void 0;
1251
1274
  const userMessage = {
@@ -1270,7 +1293,12 @@ var ChatSession = class {
1270
1293
  upload_ids: options?.uploadIds,
1271
1294
  agent_name: options?.agentName,
1272
1295
  enable_search: options?.enableSearch,
1273
- plan_mode: options?.planMode
1296
+ plan_mode: options?.planMode,
1297
+ // Per-request model choice (client-side model selection).
1298
+ provider: options?.provider,
1299
+ model: options?.model,
1300
+ reasoning_effort: options?.reasoningEffort,
1301
+ temperature: options?.temperature
1274
1302
  };
1275
1303
  await this.processStream(request);
1276
1304
  }
@@ -1628,17 +1656,12 @@ var ChatSession = class {
1628
1656
  ]);
1629
1657
  this.messages = messagesResult.status === "fulfilled" ? messagesResult.value : [];
1630
1658
  if (eventsResult.status === "fulfilled") {
1631
- let userMessageEmitted = !userMessageContent;
1659
+ if (userMessageContent) {
1660
+ this.emit({ type: "user_message", content: userMessageContent });
1661
+ }
1632
1662
  for (const ev of eventsResult.value) {
1633
1663
  const type = ev.data.type || ev.event;
1634
1664
  if (!type || type === "done") continue;
1635
- if (!userMessageEmitted && type === "message_start") {
1636
- this.emit({
1637
- type: "user_message",
1638
- content: userMessageContent
1639
- });
1640
- userMessageEmitted = true;
1641
- }
1642
1665
  const wire = { ...ev.data, type };
1643
1666
  try {
1644
1667
  await this.dispatchWireEvent(
@@ -1782,6 +1805,11 @@ var StreamManager = class {
1782
1805
  }
1783
1806
  // ── Send ──────────────────────────────────────────────────────
1784
1807
  async send(content, options) {
1808
+ if (options?.provider == null !== (options?.model == null)) {
1809
+ throw new Error(
1810
+ "`provider` and `model` must be supplied together (client-side model selection)."
1811
+ );
1812
+ }
1785
1813
  if (this._state === "streaming") return;
1786
1814
  if (!this._activeConversationId) {
1787
1815
  const id = await this.session.createNewConversation();
@@ -1793,7 +1821,11 @@ var StreamManager = class {
1793
1821
  enableSearch: options?.enableSearch,
1794
1822
  agentName: options?.agentName,
1795
1823
  uploadIds: options?.uploadIds,
1796
- planMode: options?.planMode
1824
+ planMode: options?.planMode,
1825
+ provider: options?.provider,
1826
+ model: options?.model,
1827
+ reasoningEffort: options?.reasoningEffort,
1828
+ temperature: options?.temperature
1797
1829
  });
1798
1830
  } catch {
1799
1831
  }
@@ -1947,21 +1979,21 @@ function mapSseToChat(raw) {
1947
1979
  function replayEvents(sseEvents, userMessages, handleEvent, addBlock) {
1948
1980
  const userMsgs = userMessages.filter((m) => m.role === "user");
1949
1981
  let userIdx = 0;
1950
- let expectingUserMessage = true;
1982
+ let currentJobId = null;
1951
1983
  for (const raw of sseEvents) {
1952
1984
  const type = raw.data.type || raw.event;
1953
- if (type === "message_stop") {
1954
- expectingUserMessage = true;
1955
- }
1956
- if (type === "message_start" && expectingUserMessage && userIdx < userMsgs.length) {
1957
- const userMsg = userMsgs[userIdx];
1958
- addBlock({
1959
- type: "user",
1960
- id: `replay_user_${userIdx}`,
1961
- content: userMsg.content
1962
- });
1963
- userIdx++;
1964
- expectingUserMessage = false;
1985
+ if (!type || type === "done") continue;
1986
+ const jobId = raw.data.job_id ?? null;
1987
+ if (jobId !== null && jobId !== currentJobId) {
1988
+ currentJobId = jobId;
1989
+ if (userIdx < userMsgs.length) {
1990
+ addBlock({
1991
+ type: "user",
1992
+ id: `replay_user_${userIdx}`,
1993
+ content: userMsgs[userIdx].content
1994
+ });
1995
+ userIdx++;
1996
+ }
1965
1997
  }
1966
1998
  for (const ce of mapSseToChat(raw)) {
1967
1999
  handleEvent(ce);