@coder/ai-sdk-agent 0.2.1 → 0.4.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.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { FilePart, ToolSet, Agent, SystemModelMessage, ToolChoice, StopCondition, ToolLoopAgent } from 'ai';
2
- import { LanguageModelV3DataContent, LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3StreamResult, LanguageModelV3GenerateResult, LanguageModelV3StreamPart, LanguageModelV3Message, LanguageModelV3Prompt } from '@ai-sdk/provider';
1
+ import { FilePart, ToolSet, Agent, SystemModelMessage, ToolLoopAgentSettings, ToolChoice, StopCondition, ToolLoopAgent, UIMessage } from 'ai';
2
+ import { SharedV4FileData, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4StreamResult, LanguageModelV4GenerateResult, LanguageModelV4StreamPart, LanguageModelV4Message, LanguageModelV4Prompt } from '@ai-sdk/provider';
3
3
 
4
4
  /**
5
5
  * Bytes to upload, in any convenient shape.
@@ -38,12 +38,14 @@ declare function resolveFileContent(content: FileContent, opts?: {
38
38
  name?: string;
39
39
  }): ResolvedFile;
40
40
  /**
41
- * Convert an AI SDK file-part `data` value into {@link FileContent} for upload.
42
- * The provider receives bytes or a base64 string (and, rarely, a URL). URLs are
43
- * not expected: this model declares no `supportedUrls`, so the SDK downloads
44
- * them to bytes before calling us — a URL here is therefore an error.
41
+ * Convert an AI SDK file-part `data` value (a tagged {@link SharedV4FileData}
42
+ * union) into {@link FileContent} for upload. The provider receives inline
43
+ * bytes, a base64 string, or inline text. URLs are not expected: this model
44
+ * declares no `supportedUrls`, so the SDK downloads them to bytes before
45
+ * calling us — a URL (or an opaque provider reference) here is therefore an
46
+ * error.
45
47
  */
46
- declare function dataContentToFileContent(data: LanguageModelV3DataContent): FileContent;
48
+ declare function dataContentToFileContent(data: SharedV4FileData): FileContent;
47
49
 
48
50
  /**
49
51
  * Wire types for Coder's experimental `chatd` chat API
@@ -175,6 +177,13 @@ interface ChatMessageUsage {
175
177
  cache_creation_tokens?: number;
176
178
  cache_read_tokens?: number;
177
179
  context_limit?: number;
180
+ /**
181
+ * Cost in micro-USD, when the server prices usage. Optional: servers that
182
+ * don't price usage (or predate cost reporting) omit it.
183
+ */
184
+ total_cost_micros?: number;
185
+ /** Model runtime in milliseconds, when the server reports it. */
186
+ total_runtime_ms?: number;
178
187
  }
179
188
  interface ChatMessage {
180
189
  id: number;
@@ -235,12 +244,18 @@ interface ChatMessagesResponse {
235
244
  interface UploadChatFileResponse {
236
245
  id: string;
237
246
  }
247
+ /**
248
+ * A configured model (`GET /api/experimental/chats/model-configs`). Depending
249
+ * on the server version and how a config was created, `provider`, `model`, and
250
+ * `display_name` may be absent on individual entries, so treat them as
251
+ * optional and guard reads.
252
+ */
238
253
  interface ChatModelConfig {
239
254
  id: string;
240
- provider: string;
255
+ provider?: string;
241
256
  ai_provider_id?: string;
242
- model: string;
243
- display_name: string;
257
+ model?: string;
258
+ display_name?: string;
244
259
  enabled?: boolean;
245
260
  is_default?: boolean;
246
261
  context_limit?: number;
@@ -288,6 +303,19 @@ interface ChatStreamEvent {
288
303
  }
289
304
  /** Terminal statuses: the chat has stopped generating for this turn. */
290
305
  declare const TERMINAL_STATUSES: ReadonlySet<ChatStatus>;
306
+ /** Kinds of chat lifecycle events emitted on the per-user watch socket. */
307
+ type ChatWatchEventKind = "status_change" | "summary_change" | "title_change" | "created" | "deleted" | "diff_status_change" | "action_required" | "context_dirty";
308
+ /**
309
+ * An event from `GET /api/experimental/chats/watch`, which covers every chat
310
+ * visible to the authenticated user (not a single chat). Mirrors
311
+ * `codersdk.ChatWatchEvent`.
312
+ */
313
+ interface ChatWatchEvent {
314
+ kind: ChatWatchEventKind;
315
+ chat: Chat;
316
+ /** Pending client-executed tool calls; sent with `action_required` events. */
317
+ tool_calls?: ChatStreamToolCall[];
318
+ }
291
319
 
292
320
  /**
293
321
  * A minimal WebSocket factory abstraction so the stream reader can run on
@@ -339,6 +367,27 @@ interface StreamChatEventsOptions {
339
367
  * — that triggers generator cleanup, which closes the socket.
340
368
  */
341
369
  declare function streamChatEvents(options: StreamChatEventsOptions): AsyncGenerator<ChatStreamEvent, void, void>;
370
+ interface WatchChatEventsOptions {
371
+ baseUrl: string;
372
+ token: string;
373
+ signal?: AbortSignal;
374
+ webSocketFactory?: WebSocketFactory;
375
+ }
376
+ /**
377
+ * Opens the chatd `/watch` WebSocket — lifecycle events for every chat visible
378
+ * to the authenticated user — and yields decoded {@link ChatWatchEvent}s as an
379
+ * async iterable.
380
+ *
381
+ * Unlike the per-chat `/stream`, this is a long-lived subscription, so dropped
382
+ * connections are redialed automatically with exponential backoff (1s doubling
383
+ * to a 30s cap, reset once an event arrives). It ends in exactly three ways:
384
+ * `signal` aborting, the consumer ending iteration (`break` from `for await`,
385
+ * `iterator.return()`) — both tear the socket down promptly even while a read
386
+ * is pending — or the server rejecting the upgrade with a 4xx — bad/expired
387
+ * token, or an older Coder server without the endpoint (404) — which throws a
388
+ * terminal {@link CoderApiError} instead of retrying forever.
389
+ */
390
+ declare function watchChatEvents(options: WatchChatEventsOptions): AsyncGenerator<ChatWatchEvent, void, void>;
342
391
 
343
392
  /** A file to upload as a chat attachment. */
344
393
  interface ChatFileInput {
@@ -386,7 +435,23 @@ declare class CoderChatClient {
386
435
  limit?: number;
387
436
  }, signal?: AbortSignal): Promise<ChatMessagesResponse>;
388
437
  submitToolResults(chatId: string, req: SubmitToolResultsRequest, signal?: AbortSignal): Promise<void>;
389
- interruptChat(chatId: string, signal?: AbortSignal): Promise<Chat>;
438
+ /**
439
+ * Interrupt the chat's in-flight run (`POST /interrupt`). The server flips
440
+ * an active run to `interrupting` and responds immediately with the updated
441
+ * chat — it does not wait for the run to actually stop.
442
+ *
443
+ * Pass `{ wait: true }` to send `?wait=true`, asking the server to hold the
444
+ * response until the run has stopped. Coder servers without that param
445
+ * ignore the unknown query harmlessly and still return immediately, so
446
+ * callers should be prepared to confirm completion via the event stream.
447
+ *
448
+ * The second parameter also accepts a bare `AbortSignal` (the historical
449
+ * signature).
450
+ */
451
+ interruptChat(chatId: string, optsOrSignal?: AbortSignal | {
452
+ wait?: boolean;
453
+ signal?: AbortSignal;
454
+ }): Promise<Chat>;
390
455
  updateChat(chatId: string, req: UpdateChatRequest, signal?: AbortSignal): Promise<void>;
391
456
  /** Convenience: archive a chat (soft-hide; safe for cleanup). */
392
457
  archiveChat(chatId: string, signal?: AbortSignal): Promise<void>;
@@ -409,6 +474,17 @@ declare class CoderChatClient {
409
474
  afterId?: number;
410
475
  signal?: AbortSignal;
411
476
  }): AsyncGenerator<ChatStreamEvent, void, void>;
477
+ /**
478
+ * Watch lifecycle events (status/title changes, creation, deletion, …) for
479
+ * every chat visible to the authenticated user via the `/chats/watch`
480
+ * WebSocket. Dropped connections are redialed with exponential backoff; the
481
+ * iteration ends only when `opts.signal` aborts, or with a terminal
482
+ * {@link CoderApiError} when the server rejects the upgrade with a 4xx
483
+ * (bad/expired token, or an older Coder server without the endpoint → 404).
484
+ */
485
+ watchChats(opts?: {
486
+ signal?: AbortSignal;
487
+ }): AsyncGenerator<ChatWatchEvent, void, void>;
412
488
  /**
413
489
  * Resolves a user-friendly model hint to a model-config UUID.
414
490
  *
@@ -416,10 +492,24 @@ declare class CoderChatClient {
416
492
  * (e.g. `anthropic:claude-haiku-4-5-20251001`), a bare model id, or a
417
493
  * display-name substring (case-insensitive). Returns `undefined` if no
418
494
  * match is found, in which case the caller should let chatd pick the default.
495
+ *
496
+ * Tolerates partial payloads from older/newer servers: a malformed listing
497
+ * (empty body, non-array JSON, null entries) resolves as no match, and
498
+ * entries missing `provider`, `model`, or `display_name` are matched on the
499
+ * fields they do carry — an entry without `provider` still matches by its
500
+ * model id, one without `model` only by display name.
419
501
  */
420
502
  resolveModelConfigId(hint: string, signal?: AbortSignal): Promise<string | undefined>;
421
503
  }
422
504
 
505
+ /** Share levels a port can be in; `owner` is the implicit default when no share exists. */
506
+ type PortShareLevel = "owner" | "authenticated" | "organization" | "public";
507
+ /**
508
+ * Share levels the upsert endpoint accepts. `owner` is rejected server-side —
509
+ * reverting to owner-only is done by deleting the share, not setting it.
510
+ */
511
+ type PreviewShareLevel = Exclude<PortShareLevel, "owner">;
512
+
423
513
  /**
424
514
  * Adapter that writes bytes onto a Coder workspace's filesystem, injected into
425
515
  * a {@link CoderAgent} to enable {@link CoderAgent.uploadToWorkspace}.
@@ -467,7 +557,7 @@ interface WorkspacePlacement {
467
557
  path: string;
468
558
  }
469
559
 
470
- type InnerAgent<TOOLS extends ToolSet> = ToolLoopAgent<never, TOOLS, never>;
560
+ type InnerAgent<TOOLS extends ToolSet> = ToolLoopAgent<never, TOOLS>;
471
561
  /** A handle to a file uploaded as a chat attachment (see {@link CoderAgent.attach}). */
472
562
  interface ChatAttachment extends UploadedChatFile {
473
563
  /**
@@ -482,7 +572,13 @@ interface ChatAttachment extends UploadedChatFile {
482
572
  toFilePart(): FilePart;
483
573
  }
484
574
  interface CoderAgentSettings<TOOLS extends ToolSet = {}> {
485
- /** A pre-built {@link CoderChatClient}. Mutually exclusive with baseUrl/token. */
575
+ /**
576
+ * A pre-built {@link CoderChatClient}, used instead of constructing one from
577
+ * `baseUrl`/`token`. The preview helpers ({@link CoderAgent.getPreview},
578
+ * {@link CoderAgent.sharePreview}) call non-chat endpoints and therefore
579
+ * still need credentials — pass `baseUrl` + `token` alongside `client` to
580
+ * enable them.
581
+ */
486
582
  client?: CoderChatClient;
487
583
  /** Coder deployment base URL, e.g. `https://dev.coder.com`. */
488
584
  baseUrl?: string;
@@ -514,8 +610,14 @@ interface CoderAgentSettings<TOOLS extends ToolSet = {}> {
514
610
  instructions?: string | SystemModelMessage | Array<SystemModelMessage>;
515
611
  /** Custom (client-executed) tools. Each should have an `execute` for scripting. */
516
612
  tools?: TOOLS;
613
+ /**
614
+ * Per-tool context values for tools that declare a `contextSchema`, keyed by
615
+ * tool name (forwarded to the AI SDK loop). Required by the SDK at runtime
616
+ * whenever a supplied tool declares contextual data.
617
+ */
618
+ toolsContext?: ToolLoopAgentSettings<never, TOOLS>["toolsContext"];
517
619
  toolChoice?: ToolChoice<NoInfer<TOOLS>>;
518
- /** Stop condition(s) for the AI SDK loop. Default `stepCountIs(64)`. */
620
+ /** Stop condition(s) for the AI SDK loop. Default `isStepCount(64)`. */
519
621
  stopWhen?: StopCondition<NoInfer<TOOLS>> | Array<StopCondition<NoInfer<TOOLS>>>;
520
622
  /**
521
623
  * SDK-level retries. Default `0`: this agent owns server-side chat state, so
@@ -534,6 +636,56 @@ interface CoderAgentSettings<TOOLS extends ToolSet = {}> {
534
636
  * non-positive means no limit.
535
637
  */
536
638
  requestTimeoutMs?: number;
639
+ /**
640
+ * Overall deadline in milliseconds for bounded cleanup: how long
641
+ * {@link CoderAgent.archive} keeps retrying while a freshly interrupted chat
642
+ * settles server-side, and how long `[Symbol.asyncDispose]` may take in
643
+ * total. Default 15 000. Primarily a test/tuning knob.
644
+ */
645
+ settleDeadlineMs?: number;
646
+ /**
647
+ * Pause in milliseconds between {@link CoderAgent.archive} retries while the
648
+ * chat settles. Default 1000. Primarily a test/tuning knob.
649
+ */
650
+ settleRetryDelayMs?: number;
651
+ }
652
+ /** Options for {@link CoderAgent.getPreview}. */
653
+ interface WorkspacePreviewOptions {
654
+ /** Workspace port the app listens on (4–5 digit ports parse most reliably). */
655
+ port: number;
656
+ /**
657
+ * Agent that serves the port. Optional when the workspace has exactly one
658
+ * agent; required when it has several (the error lists the candidates).
659
+ */
660
+ agentName?: string;
661
+ /**
662
+ * Protocol the app speaks *inside the workspace* on that port — `https` adds
663
+ * the `s` suffix (`3000s--…`) so the proxy connects over TLS. This is not
664
+ * the browser scheme, which follows the deployment's access URL.
665
+ * Default `"http"`.
666
+ */
667
+ protocol?: "http" | "https";
668
+ signal?: AbortSignal;
669
+ }
670
+ /** A resolved workspace preview URL (see {@link CoderAgent.getPreview}). */
671
+ interface WorkspacePreview {
672
+ /** Browser-openable subdomain app URL, e.g. `https://3000--main--dev--alice.apps.example.com`. */
673
+ url: string;
674
+ }
675
+ /** Options for {@link CoderAgent.sharePreview}. */
676
+ interface SharePreviewOptions extends WorkspacePreviewOptions {
677
+ /**
678
+ * Who may open the preview: `authenticated` (any logged-in user; the
679
+ * default), `organization` (members of the workspace's organization;
680
+ * requires a newer Coder server), or `public` (no auth at all — mind what
681
+ * the port serves).
682
+ */
683
+ shareLevel?: PreviewShareLevel;
684
+ }
685
+ /** A shared workspace preview (see {@link CoderAgent.sharePreview}). */
686
+ interface SharedWorkspacePreview extends WorkspacePreview {
687
+ /** The share level now in effect for the port. */
688
+ shareLevel: PreviewShareLevel;
537
689
  }
538
690
  /**
539
691
  * An AI SDK-compliant agent backed by a remote Coder `chatd` agent runtime.
@@ -547,7 +699,7 @@ interface CoderAgentSettings<TOOLS extends ToolSet = {}> {
547
699
  * is created on the first `generate()`/`stream()` and reused for subsequent
548
700
  * turns. Use {@link CoderAgent.resetSession} to start a fresh chat.
549
701
  */
550
- declare class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOOLS, never> {
702
+ declare class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOOLS> {
551
703
  #private;
552
704
  readonly version: "agent-v1";
553
705
  constructor(settings: CoderAgentSettings<TOOLS>);
@@ -567,16 +719,34 @@ declare class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOO
567
719
  listModels(signal?: AbortSignal): Promise<ChatModelConfig[]>;
568
720
  /** Start a fresh chatd chat on the next turn. */
569
721
  resetSession(): void;
570
- /** Interrupt the in-flight generation, if any. */
571
- interrupt(): Promise<void>;
572
- /** Archive the underlying chat (safe cleanup; hides it from listings). */
573
- archive(): Promise<void>;
722
+ /**
723
+ * Interrupt the in-flight generation, if any. Resolves as soon as the server
724
+ * acknowledges the interrupt — the run keeps winding down asynchronously for
725
+ * a few seconds afterwards (see {@link CoderAgent.archive}).
726
+ */
727
+ interrupt(opts?: {
728
+ signal?: AbortSignal;
729
+ }): Promise<void>;
730
+ /**
731
+ * Archive the underlying chat (safe cleanup; hides it from listings).
732
+ *
733
+ * A freshly interrupted/settled chat can keep winding down server-side for a
734
+ * few seconds, during which the server rejects archiving with a 409. Those
735
+ * 409s are retried (~1s apart, ~15s overall — see `settleDeadlineMs` /
736
+ * `settleRetryDelayMs`) and the last one is rethrown if the chat never
737
+ * settles; any other failure, including a caller abort, rethrows immediately.
738
+ */
739
+ archive(opts?: {
740
+ signal?: AbortSignal;
741
+ }): Promise<void>;
574
742
  /**
575
743
  * Clean up the chat when the agent leaves an `await using` scope, so cleanup
576
744
  * rides scope exit instead of a separate call you have to remember in a
577
- * `finally`. Interrupts any in-flight server run, then archives the chat.
578
- * Best-effort: disposal errors are swallowed so they can't mask the scope's
579
- * own error.
745
+ * `finally`. Interrupts any in-flight server run, then archives the chat
746
+ * (retrying while the interrupted run settles). Best-effort and bounded
747
+ * (~15s overall): disposal errors are swallowed after the bounded attempts
748
+ * so they can't mask the scope's own error — call {@link CoderAgent.archive}
749
+ * directly when you need guaranteed cleanup.
580
750
  *
581
751
  * @example
582
752
  * ```ts
@@ -615,6 +785,26 @@ declare class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOO
615
785
  path: string;
616
786
  signal?: AbortSignal;
617
787
  }): Promise<WorkspacePlacement>;
788
+ /**
789
+ * Resolve the browser URL where a port on the bound workspace can be
790
+ * previewed, e.g. `https://3000--main--dev--alice.apps.example.com`.
791
+ * Composed from stable v2 endpoints (workspace lookup + wildcard app host),
792
+ * so it works against old Coder servers. Requires the `workspaceId` setting,
793
+ * `baseUrl`/`token` credentials, and a deployment with a wildcard access URL
794
+ * configured.
795
+ *
796
+ * The URL honors the port's current share level — private to the workspace
797
+ * owner unless shared (see {@link CoderAgent.sharePreview}).
798
+ */
799
+ getPreview(opts: WorkspacePreviewOptions): Promise<WorkspacePreview>;
800
+ /**
801
+ * Share a port on the bound workspace and return its preview URL. Upserts
802
+ * the port's share level — `authenticated` by default — so teammates (or,
803
+ * with `public`, anyone) can open what the agent is serving; re-invoking
804
+ * updates the level in place. On Coder servers that predate port sharing
805
+ * (< v2.9) this fails with a 404 {@link CoderApiError} saying so.
806
+ */
807
+ sharePreview(opts: SharePreviewOptions): Promise<SharedWorkspacePreview>;
618
808
  }
619
809
 
620
810
  interface CoderLanguageModelConfig {
@@ -642,7 +832,7 @@ interface CoderLanguageModelConfig {
642
832
  requestTimeoutMs?: number;
643
833
  }
644
834
  /**
645
- * A {@link LanguageModelV3} that is backed by a remote Coder `chatd` agent
835
+ * A {@link LanguageModelV4} that is backed by a remote Coder `chatd` agent
646
836
  * runtime instead of a raw LLM. One model instance owns one chatd chat
647
837
  * (a "session"): the chat is created lazily on the first turn and reused for
648
838
  * subsequent turns and for client-tool resume steps.
@@ -657,9 +847,9 @@ interface CoderLanguageModelConfig {
657
847
  * NOTE: a single model instance is single-flight — do not run concurrent
658
848
  * generations against the same instance/session.
659
849
  */
660
- declare class CoderLanguageModel implements LanguageModelV3 {
850
+ declare class CoderLanguageModel implements LanguageModelV4 {
661
851
  #private;
662
- readonly specificationVersion: "v3";
852
+ readonly specificationVersion: "v4";
663
853
  readonly provider = "coder.chatd";
664
854
  readonly modelId: string;
665
855
  readonly supportedUrls: Record<string, RegExp[]>;
@@ -667,17 +857,20 @@ declare class CoderLanguageModel implements LanguageModelV3 {
667
857
  get chatId(): string | undefined;
668
858
  /** Drops the current session so the next turn creates a fresh chat. */
669
859
  resetSession(): void;
670
- doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult>;
671
- doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult>;
860
+ doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult>;
861
+ doGenerate(options: LanguageModelV4CallOptions): Promise<LanguageModelV4GenerateResult>;
672
862
  }
673
863
 
674
864
  /**
675
865
  * Translates one chatd turn's {@link ChatStreamEvent} stream into a sequence of
676
- * `LanguageModelV3StreamPart`s for the AI SDK.
866
+ * `LanguageModelV4StreamPart`s for the AI SDK.
677
867
  *
678
- * Two text/reasoning modes, decided per turn from the wire behavior:
868
+ * Two text/reasoning modes, decided per assistant message from the wire
869
+ * behavior:
679
870
  * - **delta mode** — chatd streamed `message_part` deltas; we emit those and
680
- * treat the trailing full `message` snapshot as a no-op for text/reasoning.
871
+ * treat the message's trailing full `message` snapshot as a no-op for
872
+ * text/reasoning. Deltas carry no message id, so "deltas arrived since the
873
+ * last assistant snapshot" is what marks a snapshot as trailing.
681
874
  * - **snapshot mode** — fast turns where only full `message` snapshots arrive;
682
875
  * we diff each snapshot's full text against what we've already emitted.
683
876
  * Both paths track an emitted-length cursor, so neither double-counts.
@@ -696,9 +889,57 @@ declare class TurnTranslator {
696
889
  get clientToolCallSeen(): boolean;
697
890
  get maxMessageId(): number;
698
891
  get error(): ChatErrorPayload | undefined;
699
- ingest(ev: ChatStreamEvent): LanguageModelV3StreamPart[];
700
- finish(): LanguageModelV3StreamPart[];
892
+ ingest(ev: ChatStreamEvent): LanguageModelV4StreamPart[];
893
+ finish(): LanguageModelV4StreamPart[];
894
+ }
895
+
896
+ /** Options for {@link chatMessagesToUIMessages}. */
897
+ interface ChatMessagesToUIMessagesOptions {
898
+ /**
899
+ * Resolves a persisted `file` part to a URL for its `FileUIPart`. Persisted
900
+ * file parts carry only a `file_id` (no bytes, and usually no `url`), so
901
+ * without a URL there is nothing for the UI to render and the part is
902
+ * skipped. Download the bytes with `CoderChatClient.getChatFile(fileId)`
903
+ * and return a data:/object/proxy URL here to keep attachments visible.
904
+ * Returning `undefined` falls back to the part's own `url`, if any.
905
+ */
906
+ fileUrl?: (part: ChatMessagePart) => string | undefined;
701
907
  }
908
+ /**
909
+ * Converts persisted chat history (`CoderChatClient.getMessages`) into AI SDK
910
+ * v6 `UIMessage`s, e.g. to rehydrate `useChat({ messages })` for an existing
911
+ * chat. Pure and side-effect free.
912
+ *
913
+ * The mapping mirrors what a live-streamed transcript of the same turn looks
914
+ * like, with one caveat around client-tool typing (below):
915
+ * - One `ChatMessage` becomes one `UIMessage` (ids stringified), sorted
916
+ * chronologically by id regardless of input order — the messages endpoint
917
+ * pages newest-first by default, while `useChat` needs oldest-first. The
918
+ * exception is `role: "tool"` messages: their `tool-result` parts are
919
+ * folded into the originating assistant `dynamic-tool` part and the message
920
+ * itself is dropped.
921
+ * - `text`/`reasoning` parts map to their UI counterparts with
922
+ * `state: "done"` (history is complete, never streaming).
923
+ * - Every `tool-call` part becomes a `dynamic-tool` part, in state
924
+ * `"output-available"` once a result exists (`"output-error"` when it is
925
+ * marked `is_error`), or `"input-available"` while no result has been
926
+ * persisted yet. For server-executed tools this matches the live stream
927
+ * (they stream with `dynamic: true`); client (`ToolSet`) tools stream live
928
+ * as statically-typed `tool-{name}` parts, but persisted history does not
929
+ * record which names were client tools, so they rehydrate as `dynamic-tool`
930
+ * too. Render tools by name — ai's `isToolOrDynamicToolUIPart` +
931
+ * `getToolOrDynamicToolName` — rather than exact `part.type` matches and
932
+ * both shapes look identical.
933
+ * - `source` parts become `source-url` parts; skipped when the wire part has
934
+ * no `url`.
935
+ * - `file` parts become `file` UI parts only when a URL is available (see
936
+ * {@link ChatMessagesToUIMessagesOptions.fileUrl}): persisted file parts
937
+ * carry only a `file_id`, and `FileUIPart` requires a `url`. Parts without
938
+ * one are skipped.
939
+ * - `file-reference` / `context-file` / `skill` / unknown part kinds have no
940
+ * UI equivalent and are skipped silently (forward compatible).
941
+ */
942
+ declare function chatMessagesToUIMessages(messages: ChatMessage[], opts?: ChatMessagesToUIMessagesOptions): UIMessage[];
702
943
 
703
944
  /** Namespace key for this package's options on AI SDK message-part `providerOptions`. */
704
945
  declare const CODER_PROVIDER_OPTIONS = "coder";
@@ -708,9 +949,9 @@ interface CoderFileProviderOptions {
708
949
  fileId?: string;
709
950
  }
710
951
  /** Concatenated system messages, mapped to chatd's `system_prompt`. */
711
- declare function extractSystemPrompt(prompt: LanguageModelV3Prompt): string | undefined;
952
+ declare function extractSystemPrompt(prompt: LanguageModelV4Prompt): string | undefined;
712
953
  /** The content array of a user message (text and file parts). */
713
- type UserContent = Extract<LanguageModelV3Message, {
954
+ type UserContent = Extract<LanguageModelV4Message, {
714
955
  role: "user";
715
956
  }>["content"];
716
957
  /**
@@ -719,7 +960,7 @@ type UserContent = Extract<LanguageModelV3Message, {
719
960
  * here so {@link userContentToInputParts} stays pure and testable.
720
961
  */
721
962
  type FilePartUploader = (file: {
722
- data: LanguageModelV3DataContent;
963
+ data: SharedV4FileData;
723
964
  mediaType: string;
724
965
  filename?: string;
725
966
  }) => Promise<string>;
@@ -745,11 +986,11 @@ type TurnAction = {
745
986
  * those tool results (the AI SDK executed a client tool between steps);
746
987
  * - otherwise the trailing `user` message → a new turn.
747
988
  */
748
- declare function classifyTurnAction(prompt: LanguageModelV3Prompt): TurnAction;
989
+ declare function classifyTurnAction(prompt: LanguageModelV4Prompt): TurnAction;
749
990
  /** Maps AI SDK function tools to chatd client-executed ("dynamic") tools. */
750
- declare function toolsToDynamicTools(tools: LanguageModelV3CallOptions["tools"]): DynamicTool[];
991
+ declare function toolsToDynamicTools(tools: LanguageModelV4CallOptions["tools"]): DynamicTool[];
751
992
  /** The set of tool names the AI SDK side owns (i.e. client-executed). */
752
- declare function dynamicToolNames(tools: LanguageModelV3CallOptions["tools"]): Set<string>;
993
+ declare function dynamicToolNames(tools: LanguageModelV4CallOptions["tools"]): Set<string>;
753
994
 
754
995
  /** Base error for all `@coder/ai-sdk-agent` failures. */
755
996
  declare class CoderAgentError extends Error {
@@ -783,4 +1024,4 @@ declare class CoderChatError extends CoderAgentError {
783
1024
  constructor(payload: ChatErrorPayload);
784
1025
  }
785
1026
 
786
- export { CHAT_ATTACHMENT_MEDIA_TYPES, CODER_PROVIDER_OPTIONS, type Chat, type ChatAttachment, type ChatAttachmentMediaType, type ChatBusyBehavior, type ChatClientType, type ChatErrorPayload, type ChatFileInput, type ChatInputPart, type ChatInputPartType, type ChatMessage, type ChatMessagePart, type ChatMessagePartType, type ChatMessageRole, type ChatMessageUsage, type ChatMessagesResponse, type ChatModelConfig, type ChatPlanMode, type ChatQueuedMessage, type ChatStatus, type ChatStreamActionRequired, type ChatStreamEvent, type ChatStreamEventType, type ChatStreamMessagePart, type ChatStreamRetry, type ChatStreamStatus, type ChatStreamToolCall, CoderAgent, CoderAgentError, type CoderAgentSettings, CoderApiError, CoderChatClient, type CoderChatClientOptions, CoderChatError, type CoderFileProviderOptions, CoderLanguageModel, type CoderLanguageModelConfig, type CreateChatMessageRequest, type CreateChatMessageResponse, type CreateChatRequest, type DynamicTool, type FileContent, type FilePartUploader, MAX_CHAT_FILE_SIZE_BYTES, type ResolvedFile, type SubmitToolResultsRequest, TERMINAL_STATUSES, type ToolResult, type TurnAction, TurnTranslator, type UpdateChatRequest, type UploadChatFileResponse, type UploadedChatFile, type UserContent, type WebSocketFactory, type WebSocketLike, type WorkspaceFileStore, type WorkspacePlacement, classifyTurnAction, dataContentToFileContent, dynamicToolNames, extractSystemPrompt, resolveFileContent, streamChatEvents, toolsToDynamicTools, userContentToInputParts };
1027
+ export { CHAT_ATTACHMENT_MEDIA_TYPES, CODER_PROVIDER_OPTIONS, type Chat, type ChatAttachment, type ChatAttachmentMediaType, type ChatBusyBehavior, type ChatClientType, type ChatErrorPayload, type ChatFileInput, type ChatInputPart, type ChatInputPartType, type ChatMessage, type ChatMessagePart, type ChatMessagePartType, type ChatMessageRole, type ChatMessageUsage, type ChatMessagesResponse, type ChatMessagesToUIMessagesOptions, type ChatModelConfig, type ChatPlanMode, type ChatQueuedMessage, type ChatStatus, type ChatStreamActionRequired, type ChatStreamEvent, type ChatStreamEventType, type ChatStreamMessagePart, type ChatStreamRetry, type ChatStreamStatus, type ChatStreamToolCall, type ChatWatchEvent, type ChatWatchEventKind, CoderAgent, CoderAgentError, type CoderAgentSettings, CoderApiError, CoderChatClient, type CoderChatClientOptions, CoderChatError, type CoderFileProviderOptions, CoderLanguageModel, type CoderLanguageModelConfig, type CreateChatMessageRequest, type CreateChatMessageResponse, type CreateChatRequest, type DynamicTool, type FileContent, type FilePartUploader, MAX_CHAT_FILE_SIZE_BYTES, type PreviewShareLevel, type ResolvedFile, type SharePreviewOptions, type SharedWorkspacePreview, type SubmitToolResultsRequest, TERMINAL_STATUSES, type ToolResult, type TurnAction, TurnTranslator, type UpdateChatRequest, type UploadChatFileResponse, type UploadedChatFile, type UserContent, type WatchChatEventsOptions, type WebSocketFactory, type WebSocketLike, type WorkspaceFileStore, type WorkspacePlacement, type WorkspacePreview, type WorkspacePreviewOptions, chatMessagesToUIMessages, classifyTurnAction, dataContentToFileContent, dynamicToolNames, extractSystemPrompt, resolveFileContent, streamChatEvents, toolsToDynamicTools, userContentToInputParts, watchChatEvents };