@coder/ai-sdk-agent 0.4.2 → 0.5.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/README.md +33 -5
- package/dist/index.d.ts +98 -6
- package/dist/index.js +595 -92
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -311,9 +311,27 @@ tools runs several segments, so it bounds each one, not the whole call. To cap t
|
|
|
311
311
|
await agent.generate({ prompt: "…", abortSignal: AbortSignal.timeout(120_000) });
|
|
312
312
|
```
|
|
313
313
|
|
|
314
|
-
If the event stream drops
|
|
315
|
-
|
|
316
|
-
|
|
314
|
+
If the event stream drops mid‑turn, the agent redials it automatically with
|
|
315
|
+
exponential backoff, replaying the turn's events from its starting cursor and
|
|
316
|
+
deduplicating them on receipt — the server keeps generating during the gap, so
|
|
317
|
+
a transient drop costs nothing and the run is **not** interrupted. Only when the stream cannot be re‑established (several
|
|
318
|
+
consecutive failed attempts, ~15s) is the server run interrupted and the call
|
|
319
|
+
rejected with a `CoderStreamError` — an AI SDK `APICallError`. When the failed
|
|
320
|
+
turn had just created its chat AND had no external effects a replay would
|
|
321
|
+
repeat — no `workspaceId`, no `mcpServerIds`, and no freshly uploaded inline
|
|
322
|
+
attachments (pre‑uploaded `fileId` references are fine) — the dead session is
|
|
323
|
+
discarded and the error is `isRetryable: true`, so `generate()` calls with
|
|
324
|
+
`maxRetries` set retry the whole turn on a fresh chat automatically. Otherwise
|
|
325
|
+
the error is `isRetryable: false` and retrying is the caller's deliberate
|
|
326
|
+
decision: on a chat with prior state (resumed sessions, later turns,
|
|
327
|
+
tool‑result segments) a re‑invocation would resubmit the same prompt as a new
|
|
328
|
+
user turn, workspace/MCP tools may already have executed side effects, and
|
|
329
|
+
inline attachments would upload again. (For `stream()`, a mid‑stream failure
|
|
330
|
+
surfaces on the stream itself, outside the SDK's retry wrapper — handle it in
|
|
331
|
+
your consumption loop.)
|
|
332
|
+
A non‑transient 4xx upgrade rejection (bad/expired token,
|
|
333
|
+
deleted chat) fails fast with a `CoderApiError` instead of retrying; 408/425/429
|
|
334
|
+
consume the redial budget like any other transient failure.
|
|
317
335
|
|
|
318
336
|
## Cleanup
|
|
319
337
|
|
|
@@ -344,10 +362,20 @@ avoids accumulating live chats.
|
|
|
344
362
|
|
|
345
363
|
## Handling errors
|
|
346
364
|
|
|
347
|
-
All errors extend `CoderAgentError
|
|
365
|
+
All errors extend `CoderAgentError`, except `CoderStreamError` (below). Two
|
|
366
|
+
carry structured detail you can branch on:
|
|
348
367
|
|
|
349
368
|
- **`CoderApiError`** — an HTTP request failed. Fields: `status`, `method`, `path`, `detail`.
|
|
350
369
|
- **`CoderChatError`** — a turn ended in an error, timed out, or lost its stream. Fields: `kind`, `retryable`, `statusCode`, `provider`.
|
|
370
|
+
- **`CoderStreamError`** — the event stream dropped and could not be re‑established
|
|
371
|
+
within its redial budget. Extends the AI SDK's `APICallError` (not
|
|
372
|
+
`CoderAgentError`), so `generate()`'s `maxRetries` machinery recognizes it.
|
|
373
|
+
`isRetryable` is `true` only when the failed turn created its chat (the dead
|
|
374
|
+
session is discarded, so a retry starts fresh) and had no external effects to
|
|
375
|
+
repeat (workspace/MCP tooling, fresh attachment uploads); otherwise it is
|
|
376
|
+
`false` — a retry would resubmit the prompt as a new user turn and could
|
|
377
|
+
duplicate those effects. A failure mid‑`stream()` surfaces on the stream,
|
|
378
|
+
outside the retry wrapper. The last transport failure is in `cause`.
|
|
351
379
|
|
|
352
380
|
```ts
|
|
353
381
|
import { CoderApiError, CoderChatError } from "@coder/ai-sdk-agent";
|
|
@@ -613,7 +641,7 @@ what's available.
|
|
|
613
641
|
```
|
|
614
642
|
CoderAgent (implements ai.Agent)
|
|
615
643
|
└─ ToolLoopAgent (ai) ← inherits generate()/stream(), loop control
|
|
616
|
-
└─ CoderLanguageModel ← implements @ai-sdk/provider
|
|
644
|
+
└─ CoderLanguageModel ← implements @ai-sdk/provider LanguageModelV4
|
|
617
645
|
└─ CoderChatClient ← REST + WebSocket to /api/experimental/chats
|
|
618
646
|
└─ Coder Agents ← runs the agent loop SERVER-side
|
|
619
647
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
import { SharedV4FileData, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4StreamResult, LanguageModelV4GenerateResult, LanguageModelV4StreamPart, LanguageModelV4Message, LanguageModelV4Prompt, APICallError } from '@ai-sdk/provider';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Bytes to upload, in any convenient shape.
|
|
@@ -365,6 +365,29 @@ interface StreamChatEventsOptions {
|
|
|
365
365
|
* The chat stream is a live subscription that stays open after a turn settles,
|
|
366
366
|
* so callers should `break` out of the loop once they observe a terminal status
|
|
367
367
|
* — that triggers generator cleanup, which closes the socket.
|
|
368
|
+
*
|
|
369
|
+
* Dropped connections are redialed with exponential backoff (like
|
|
370
|
+
* {@link watchChatEvents}), replaying from the turn's ORIGINAL `after_id`. The
|
|
371
|
+
* cursor is deliberately NOT advanced past messages already yielded: chatd's
|
|
372
|
+
* initial sync filters out ids at or below `after_id`, so an advanced cursor
|
|
373
|
+
* would silently lose same-id REVISION snapshots (updated content, tool
|
|
374
|
+
* results, usage) committed while the connection was down. Consumers must
|
|
375
|
+
* dedupe repeated `message` snapshots — the wire contract demands that anyway,
|
|
376
|
+
* since chatd re-sends full snapshots on revision bumps even without a
|
|
377
|
+
* reconnect (TurnTranslator does). On reconnect chatd also replays the
|
|
378
|
+
* in-progress generation attempt's `message_part` deltas from `seq` 1, so
|
|
379
|
+
* parts of an episode
|
|
380
|
+
* (`history_version`, `generation_attempt`) at or below the last yielded `seq`
|
|
381
|
+
* are suppressed here rather than double-yielded. The iteration ends in five
|
|
382
|
+
* ways: `signal` aborting (clean return), a non-transient 4xx upgrade
|
|
383
|
+
* rejection (terminal {@link CoderApiError} — bad/expired token or a deleted
|
|
384
|
+
* chat cannot succeed on retry, while 408/425/429 consume the redial budget),
|
|
385
|
+
* an unparseable frame (terminal {@link CoderAgentError} — a redial would
|
|
386
|
+
* replay the same frame forever), a drop while deltas WITHOUT episode
|
|
387
|
+
* coordinates are outstanding (older chatd; a {@link CoderStreamError},
|
|
388
|
+
* because a delta replay could not be deduped and would double-emit — a NEW
|
|
389
|
+
* committed `message` finalizes them into dedupable snapshots and re-enables
|
|
390
|
+
* redial), or the redial budget running out (also a {@link CoderStreamError}).
|
|
368
391
|
*/
|
|
369
392
|
declare function streamChatEvents(options: StreamChatEventsOptions): AsyncGenerator<ChatStreamEvent, void, void>;
|
|
370
393
|
interface WatchChatEventsOptions {
|
|
@@ -518,7 +541,9 @@ type PreviewShareLevel = Exclude<PortShareLevel, "owner">;
|
|
|
518
541
|
* binaries, oversized files, or anything outside the narrow chat-attachment
|
|
519
542
|
* allowlist. It is intentionally NOT bundled into the agent package: the agent
|
|
520
543
|
* core stays dependency-free, and whoever already holds a workspace connection
|
|
521
|
-
* (e.g. a `@coder/ai-sdk-
|
|
544
|
+
* (e.g. a `CoderWorkspaceSession` from `@coder/ai-sdk-sandbox`) supplies a
|
|
545
|
+
* few-line adapter. Note the session does not report the workspace UUID —
|
|
546
|
+
* retain it from `ensureCoderWorkspace(...).id` (same package).
|
|
522
547
|
*
|
|
523
548
|
* The store does one thing — write bytes to a path. It deliberately does not
|
|
524
549
|
* unpack archives or mutate the working tree: instruct the agent to `unzip`/
|
|
@@ -526,8 +551,10 @@ type PreviewShareLevel = Exclude<PortShareLevel, "owner">;
|
|
|
526
551
|
*
|
|
527
552
|
* @example
|
|
528
553
|
* ```ts
|
|
554
|
+
* // UUID via `ensureCoderWorkspace`; bytes over a `CoderWorkspaceSession`.
|
|
555
|
+
* const ws = await ensureCoderWorkspace({ workspace: "my-ws" });
|
|
529
556
|
* const store: WorkspaceFileStore = {
|
|
530
|
-
* workspaceId:
|
|
557
|
+
* workspaceId: ws.id!, // workspace UUID (reported by the CLI/native transports)
|
|
531
558
|
* write: async ({ content, path, signal }) => {
|
|
532
559
|
* await session.writeFile({ path, content: toStream(content), abortSignal: signal });
|
|
533
560
|
* return { path };
|
|
@@ -595,7 +622,7 @@ interface CoderAgentSettings<TOOLS extends ToolSet = {}> {
|
|
|
595
622
|
/**
|
|
596
623
|
* Adapter for writing files onto a workspace filesystem, enabling
|
|
597
624
|
* {@link CoderAgent.uploadToWorkspace}. Supply one backed by a workspace
|
|
598
|
-
* connection (e.g. a `@coder/ai-sdk-
|
|
625
|
+
* connection (e.g. a `CoderWorkspaceSession` from `@coder/ai-sdk-sandbox`).
|
|
599
626
|
*/
|
|
600
627
|
workspaceFiles?: WorkspaceFileStore;
|
|
601
628
|
/** chatd-side MCP servers to enable for this chat. */
|
|
@@ -846,6 +873,15 @@ interface CoderLanguageModelConfig {
|
|
|
846
873
|
*
|
|
847
874
|
* NOTE: a single model instance is single-flight — do not run concurrent
|
|
848
875
|
* generations against the same instance/session.
|
|
876
|
+
*
|
|
877
|
+
* The session retains ONE event stream per turn (issue #44): a segment that
|
|
878
|
+
* pauses for client tools leaves the WebSocket open, and the tool-result
|
|
879
|
+
* resume segment keeps reading it — no re-dial per tool round trip. The
|
|
880
|
+
* socket closes when its segment ends any other way (terminal settle, error,
|
|
881
|
+
* abort, timeout), when the next turn replaces it, or on session teardown
|
|
882
|
+
* (`resetSession()` / `[Symbol.asyncDispose]()`). A turn that pauses for
|
|
883
|
+
* client tools but never resumes leaves the socket open until teardown, so
|
|
884
|
+
* dispose or reset sessions you abandon mid-turn.
|
|
849
885
|
*/
|
|
850
886
|
declare class CoderLanguageModel implements LanguageModelV4 {
|
|
851
887
|
#private;
|
|
@@ -855,8 +891,32 @@ declare class CoderLanguageModel implements LanguageModelV4 {
|
|
|
855
891
|
readonly supportedUrls: Record<string, RegExp[]>;
|
|
856
892
|
constructor(config: CoderLanguageModelConfig);
|
|
857
893
|
get chatId(): string | undefined;
|
|
858
|
-
/**
|
|
894
|
+
/**
|
|
895
|
+
* Drops the current session so the next turn creates a fresh chat. Also
|
|
896
|
+
* closes the retained event stream, if any (fire-and-forget — a sync reset
|
|
897
|
+
* must not wait on socket teardown, and `close()` never rejects).
|
|
898
|
+
*/
|
|
859
899
|
resetSession(): void;
|
|
900
|
+
/**
|
|
901
|
+
* Closes the retained event stream, if any. The stream otherwise closes
|
|
902
|
+
* when its turn settles or fails, or when the next turn replaces it — but a
|
|
903
|
+
* turn that pauses for client tools and never resumes (e.g. the caller
|
|
904
|
+
* stopped the tool loop, or aborted between segments) leaves the socket
|
|
905
|
+
* open, so dispose (`await using`) or {@link resetSession} sessions you
|
|
906
|
+
* abandon mid-turn.
|
|
907
|
+
*/
|
|
908
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
909
|
+
/**
|
|
910
|
+
* Closes a stream retained by a client-tool pause. Called on explicit
|
|
911
|
+
* interrupts ({@link CoderAgent.interrupt}): an interrupted pause is dead —
|
|
912
|
+
* its buffer would otherwise collect the interrupt's own settle events,
|
|
913
|
+
* which a later tool-result resume would consume as if they were the
|
|
914
|
+
* resumed generation's (truncating the turn), and if the tool loop never
|
|
915
|
+
* resumes the socket would outlive the explicit interrupt. A stream a
|
|
916
|
+
* segment is actively reading is left alone: that segment observes the
|
|
917
|
+
* settle the interrupt produces and closes the stream itself.
|
|
918
|
+
*/
|
|
919
|
+
closePausedStream(): Promise<void>;
|
|
860
920
|
doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult>;
|
|
861
921
|
doGenerate(options: LanguageModelV4CallOptions): Promise<LanguageModelV4GenerateResult>;
|
|
862
922
|
}
|
|
@@ -884,6 +944,15 @@ declare class TurnTranslator {
|
|
|
884
944
|
constructor(opts: {
|
|
885
945
|
dynamicToolNames: ReadonlySet<string>;
|
|
886
946
|
turnCursor?: number;
|
|
947
|
+
/**
|
|
948
|
+
* Tool-call ids whose results the session already submitted. With the
|
|
949
|
+
* stream retained across segments (and redialed with the turn's original
|
|
950
|
+
* cursor), a reconnect can replay a previous segment's `action_required`
|
|
951
|
+
* snapshot; re-emitting an already-answered call would make the AI SDK
|
|
952
|
+
* execute the tool a second time. A live reference — the set grows as
|
|
953
|
+
* later segments submit results.
|
|
954
|
+
*/
|
|
955
|
+
submittedToolCallIds?: ReadonlySet<string>;
|
|
887
956
|
});
|
|
888
957
|
get terminalStatus(): ChatStatus | undefined;
|
|
889
958
|
/** Whether a client (custom) tool call has been emitted this turn. */
|
|
@@ -1015,6 +1084,29 @@ declare class CoderApiError extends CoderAgentError {
|
|
|
1015
1084
|
detail?: string;
|
|
1016
1085
|
});
|
|
1017
1086
|
}
|
|
1087
|
+
/**
|
|
1088
|
+
* The per-chat event stream dropped and could not be re-established within its
|
|
1089
|
+
* redial budget (or could not be safely resumed — see `streamChatEvents` on
|
|
1090
|
+
* servers without delta coordinates). Extends the AI SDK's {@link APICallError} with
|
|
1091
|
+
* `isRetryable: true` because the `ai` package's retry machinery (`maxRetries`)
|
|
1092
|
+
* only honors `APICallError`/`GatewayError`-branded errors — plain `Error`
|
|
1093
|
+
* subclasses are never retried no matter what they claim. Note the retry
|
|
1094
|
+
* wrapper only sees errors that reject the model call itself (`generate()`);
|
|
1095
|
+
* a failure mid-`stream()` surfaces on the stream, past that wrapper. The last
|
|
1096
|
+
* underlying transport failure is preserved as `cause`.
|
|
1097
|
+
*
|
|
1098
|
+
* NOTE: this is deliberately NOT a {@link CoderAgentError} (single
|
|
1099
|
+
* inheritance); match it with `APICallError.isInstance(err)` or `err.name`.
|
|
1100
|
+
*/
|
|
1101
|
+
declare class CoderStreamError extends APICallError {
|
|
1102
|
+
name: string;
|
|
1103
|
+
constructor(args: {
|
|
1104
|
+
message: string;
|
|
1105
|
+
url: string;
|
|
1106
|
+
cause?: unknown;
|
|
1107
|
+
isRetryable?: boolean;
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1018
1110
|
/** A chat generation ended in an error status. */
|
|
1019
1111
|
declare class CoderChatError extends CoderAgentError {
|
|
1020
1112
|
name: string;
|
|
@@ -1025,4 +1117,4 @@ declare class CoderChatError extends CoderAgentError {
|
|
|
1025
1117
|
constructor(payload: ChatErrorPayload);
|
|
1026
1118
|
}
|
|
1027
1119
|
|
|
1028
|
-
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 };
|
|
1120
|
+
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, CoderStreamError, 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 };
|