@coder/ai-sdk-agent 0.4.0 → 0.4.2
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 +26 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +73 -14
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -328,9 +328,7 @@ To make cleanup ride scope exit instead of a `finally` you have to remember, the
|
|
|
328
328
|
agent is an **async disposable**:
|
|
329
329
|
|
|
330
330
|
```ts
|
|
331
|
-
await using agent = new CoderAgent({
|
|
332
|
-
/* … */
|
|
333
|
-
});
|
|
331
|
+
await using agent = new CoderAgent({/* … */});
|
|
334
332
|
const { text } = await agent.generate({ prompt: "…" });
|
|
335
333
|
// agent.interrupt() + agent.archive() run automatically when the scope exits.
|
|
336
334
|
```
|
|
@@ -373,14 +371,31 @@ retrying the whole step deliberately.
|
|
|
373
371
|
|
|
374
372
|
## Usage & cost
|
|
375
373
|
|
|
376
|
-
Results carry normalized token usage in `usage
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
374
|
+
Results carry normalized token usage in `usage`. A chatd turn runs several
|
|
375
|
+
model steps server‑side (one per server tool round), each reporting its own
|
|
376
|
+
usage — the SDK **sums every step**, and the AI SDK adds up the steps of a
|
|
377
|
+
turn that paused for client tools, so `result.usage` reflects what the whole
|
|
378
|
+
turn actually consumed. `inputTokens` is the full prompt size: Coder
|
|
379
|
+
normalizes the wire `input_tokens` to the _uncached_ count (cache reads/writes
|
|
380
|
+
are separate fields), so the SDK adds them back into the total and exposes the
|
|
381
|
+
split via `inputTokenDetails` (`noCacheTokens`, `cacheReadTokens`,
|
|
382
|
+
`cacheWriteTokens`).
|
|
383
|
+
|
|
384
|
+
The snake_case wire usage lives **per step** at `result.steps[i].usage.raw`
|
|
385
|
+
(the AI SDK does not carry `raw` onto the summed `result.usage`) for fields
|
|
386
|
+
the normalized shape has no slot for (`context_limit`, cost, runtime, and any
|
|
387
|
+
newer wire fields, which pass through newest‑value‑wins). `raw` keeps the wire
|
|
388
|
+
convention (`input_tokens` = uncached only) with counters summed over that
|
|
389
|
+
step's server‑side model steps and `context_limit` from the newest one — so
|
|
390
|
+
don't divide `raw`'s summed counters by `context_limit` to estimate context
|
|
391
|
+
fullness; they are turn consumption, not a prompt‑size snapshot. When the
|
|
392
|
+
server reports them, `total_cost_micros` (micro‑USD) and `total_runtime_ms`
|
|
393
|
+
are mirrored the same way under each step's `providerMetadata.coder`
|
|
394
|
+
(`result.providerMetadata` reflects only the final step — sum
|
|
395
|
+
`result.steps[*].providerMetadata.coder` for whole‑turn cost when client tools
|
|
396
|
+
ran). Both are **absence‑tolerant mirrors**: on servers that don't send them
|
|
397
|
+
(cost is otherwise only on the aggregate cost endpoints,
|
|
398
|
+
`/api/experimental/chats/cost/*`), nothing is emitted.
|
|
384
399
|
|
|
385
400
|
Forward usage to a UI via message metadata:
|
|
386
401
|
|
package/dist/index.d.ts
CHANGED
|
@@ -883,6 +883,7 @@ declare class TurnTranslator {
|
|
|
883
883
|
#private;
|
|
884
884
|
constructor(opts: {
|
|
885
885
|
dynamicToolNames: ReadonlySet<string>;
|
|
886
|
+
turnCursor?: number;
|
|
886
887
|
});
|
|
887
888
|
get terminalStatus(): ChatStatus | undefined;
|
|
888
889
|
/** Whether a client (custom) tool call has been emitted this turn. */
|
package/dist/index.js
CHANGED
|
@@ -883,11 +883,29 @@ function dynamicToolNames(tools) {
|
|
|
883
883
|
}
|
|
884
884
|
|
|
885
885
|
// src/model/translate.ts
|
|
886
|
+
function addDefined(a, b) {
|
|
887
|
+
if (b === void 0) return a;
|
|
888
|
+
return (a ?? 0) + b;
|
|
889
|
+
}
|
|
890
|
+
var USAGE_FIELD_KINDS = {
|
|
891
|
+
input_tokens: "sum",
|
|
892
|
+
output_tokens: "sum",
|
|
893
|
+
total_tokens: "sum",
|
|
894
|
+
reasoning_tokens: "sum",
|
|
895
|
+
cache_creation_tokens: "sum",
|
|
896
|
+
cache_read_tokens: "sum",
|
|
897
|
+
total_cost_micros: "sum",
|
|
898
|
+
total_runtime_ms: "sum",
|
|
899
|
+
context_limit: "newest"
|
|
900
|
+
};
|
|
886
901
|
function mapUsage(u) {
|
|
887
902
|
return {
|
|
888
903
|
inputTokens: {
|
|
889
|
-
total:
|
|
890
|
-
|
|
904
|
+
total: addDefined(
|
|
905
|
+
addDefined(u?.input_tokens, u?.cache_read_tokens),
|
|
906
|
+
u?.cache_creation_tokens
|
|
907
|
+
),
|
|
908
|
+
noCache: u?.input_tokens,
|
|
891
909
|
cacheRead: u?.cache_read_tokens,
|
|
892
910
|
cacheWrite: u?.cache_creation_tokens
|
|
893
911
|
},
|
|
@@ -896,11 +914,28 @@ function mapUsage(u) {
|
|
|
896
914
|
text: void 0,
|
|
897
915
|
reasoning: u?.reasoning_tokens
|
|
898
916
|
},
|
|
899
|
-
// Preserve the
|
|
917
|
+
// Preserve the turn's wire usage (snake_case) so callers can reach fields
|
|
900
918
|
// the normalized shape has no slot for (context_limit, cost, runtime, …).
|
|
901
919
|
...u ? { raw: u } : {}
|
|
902
920
|
};
|
|
903
921
|
}
|
|
922
|
+
function accumulateUsage(byMessageId) {
|
|
923
|
+
if (byMessageId.size === 0) return void 0;
|
|
924
|
+
const total = {};
|
|
925
|
+
const newestIdByKey = /* @__PURE__ */ new Map();
|
|
926
|
+
for (const [id, u] of byMessageId) {
|
|
927
|
+
for (const [key, value] of Object.entries(u)) {
|
|
928
|
+
if (value === void 0) continue;
|
|
929
|
+
if (USAGE_FIELD_KINDS[key] === "sum" && typeof value === "number") {
|
|
930
|
+
total[key] = (total[key] ?? 0) + value;
|
|
931
|
+
} else if (id > (newestIdByKey.get(key) ?? -1)) {
|
|
932
|
+
newestIdByKey.set(key, id);
|
|
933
|
+
total[key] = value;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return total;
|
|
938
|
+
}
|
|
904
939
|
function jsonResult(value) {
|
|
905
940
|
return value ?? {};
|
|
906
941
|
}
|
|
@@ -919,12 +954,24 @@ var TurnTranslator = class {
|
|
|
919
954
|
#clientToolCalls = /* @__PURE__ */ new Set();
|
|
920
955
|
#clientToolCallSeen = false;
|
|
921
956
|
#sources = /* @__PURE__ */ new Set();
|
|
922
|
-
|
|
957
|
+
// Per-message wire usage, keyed by message id so a re-streamed revision of
|
|
958
|
+
// the same message replaces its earlier entry instead of double-counting
|
|
959
|
+
// (chatd re-sends full snapshots on revision bumps and history resets).
|
|
960
|
+
#usageByMessageId = /* @__PURE__ */ new Map();
|
|
923
961
|
#error;
|
|
924
962
|
#terminalStatus;
|
|
925
963
|
#maxMessageId = 0;
|
|
964
|
+
/**
|
|
965
|
+
* Messages with an id at or below this cursor belong to earlier turns (or
|
|
966
|
+
* to a segment that already streamed) — they arrive only as replays: the
|
|
967
|
+
* initial sync when resuming a chat, or a mid-turn `history_reset` re-send
|
|
968
|
+
* of the full history. Ingest skips them entirely so a replay can neither
|
|
969
|
+
* re-emit earlier turns' content nor inflate this turn's usage.
|
|
970
|
+
*/
|
|
971
|
+
#turnCursor;
|
|
926
972
|
constructor(opts) {
|
|
927
973
|
this.#dynamicToolNames = opts.dynamicToolNames;
|
|
974
|
+
this.#turnCursor = opts.turnCursor ?? 0;
|
|
928
975
|
}
|
|
929
976
|
get terminalStatus() {
|
|
930
977
|
return this.#terminalStatus;
|
|
@@ -1144,8 +1191,9 @@ var TurnTranslator = class {
|
|
|
1144
1191
|
}
|
|
1145
1192
|
#ingestMessage(out, message) {
|
|
1146
1193
|
if (message.id > this.#maxMessageId) this.#maxMessageId = message.id;
|
|
1147
|
-
if (message.
|
|
1148
|
-
|
|
1194
|
+
if (message.id <= this.#turnCursor) return;
|
|
1195
|
+
if (message.usage && message.role === "assistant") {
|
|
1196
|
+
this.#usageByMessageId.set(message.id, message.usage);
|
|
1149
1197
|
}
|
|
1150
1198
|
const content = message.content ?? [];
|
|
1151
1199
|
if (message.role === "assistant") {
|
|
@@ -1190,14 +1238,13 @@ var TurnTranslator = class {
|
|
|
1190
1238
|
else if (this.#clientToolCallSeen || this.#terminalStatus === "requires_action")
|
|
1191
1239
|
unified = "tool-calls";
|
|
1192
1240
|
else unified = "stop";
|
|
1241
|
+
const usage = accumulateUsage(this.#usageByMessageId);
|
|
1193
1242
|
const coder = {};
|
|
1194
|
-
if (
|
|
1195
|
-
|
|
1196
|
-
if (this.#usage?.total_runtime_ms !== void 0)
|
|
1197
|
-
coder.total_runtime_ms = this.#usage.total_runtime_ms;
|
|
1243
|
+
if (usage?.total_cost_micros !== void 0) coder.total_cost_micros = usage.total_cost_micros;
|
|
1244
|
+
if (usage?.total_runtime_ms !== void 0) coder.total_runtime_ms = usage.total_runtime_ms;
|
|
1198
1245
|
out.push({
|
|
1199
1246
|
type: "finish",
|
|
1200
|
-
usage: mapUsage(
|
|
1247
|
+
usage: mapUsage(usage),
|
|
1201
1248
|
finishReason: { unified, raw: this.#terminalStatus },
|
|
1202
1249
|
...Object.keys(coder).length > 0 ? { providerMetadata: { coder } } : {}
|
|
1203
1250
|
});
|
|
@@ -1284,7 +1331,7 @@ var CoderLanguageModel = class {
|
|
|
1284
1331
|
(s) => s !== void 0
|
|
1285
1332
|
);
|
|
1286
1333
|
const signal = sources.length > 0 ? AbortSignal.any(sources) : void 0;
|
|
1287
|
-
|
|
1334
|
+
let translator;
|
|
1288
1335
|
let interruptSent = false;
|
|
1289
1336
|
const interrupt = () => {
|
|
1290
1337
|
const id = this.#chatId;
|
|
@@ -1324,6 +1371,14 @@ var CoderLanguageModel = class {
|
|
|
1324
1371
|
);
|
|
1325
1372
|
}
|
|
1326
1373
|
let afterId;
|
|
1374
|
+
if (this.#chatId && this.#lastSeenMessageId === 0) {
|
|
1375
|
+
const { messages } = await this.#config.client.getMessages(
|
|
1376
|
+
this.#chatId,
|
|
1377
|
+
{ limit: 1 },
|
|
1378
|
+
signal
|
|
1379
|
+
);
|
|
1380
|
+
this.#lastSeenMessageId = messages[0]?.id ?? 0;
|
|
1381
|
+
}
|
|
1327
1382
|
if (action.kind === "new-turn") {
|
|
1328
1383
|
const [modelConfigId, content] = await Promise.all([
|
|
1329
1384
|
this.#resolveModelConfigId(signal),
|
|
@@ -1370,6 +1425,10 @@ var CoderLanguageModel = class {
|
|
|
1370
1425
|
afterId = this.#lastSeenMessageId;
|
|
1371
1426
|
}
|
|
1372
1427
|
const chatId = this.#chatId;
|
|
1428
|
+
translator = new TurnTranslator({
|
|
1429
|
+
dynamicToolNames: dynamicToolNames(options.tools),
|
|
1430
|
+
turnCursor: afterId ?? 0
|
|
1431
|
+
});
|
|
1373
1432
|
let sinceRequiresAction = 0;
|
|
1374
1433
|
try {
|
|
1375
1434
|
for await (const ev of this.#config.client.streamEvents(chatId, { afterId, signal })) {
|
|
@@ -1405,10 +1464,10 @@ var CoderLanguageModel = class {
|
|
|
1405
1464
|
throwIfAborted();
|
|
1406
1465
|
throw err;
|
|
1407
1466
|
} finally {
|
|
1408
|
-
if (translator.maxMessageId > this.#lastSeenMessageId)
|
|
1467
|
+
if (translator && translator.maxMessageId > this.#lastSeenMessageId)
|
|
1409
1468
|
this.#lastSeenMessageId = translator.maxMessageId;
|
|
1410
1469
|
signal?.removeEventListener("abort", interrupt);
|
|
1411
|
-
if (!translator
|
|
1470
|
+
if (!translator?.terminalStatus) interrupt();
|
|
1412
1471
|
this.#inFlight = false;
|
|
1413
1472
|
}
|
|
1414
1473
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent/coder-agent.ts","../src/errors.ts","../src/ai-version.ts","../src/files.ts","../src/coder/types.ts","../src/coder/ws.ts","../src/coder/client.ts","../src/coder/workspaces.ts","../src/model/prompt.ts","../src/model/translate.ts","../src/model/language-model.ts","../src/model/history.ts"],"sourcesContent":["import {\n type Agent,\n type FilePart,\n isStepCount,\n type StopCondition,\n type SystemModelMessage,\n type ToolChoice,\n ToolLoopAgent,\n type ToolLoopAgentSettings,\n type ToolSet,\n} from \"ai\";\nimport { assertSupportedAiVersion } from \"../ai-version.js\";\nimport {\n type ChatFileInput,\n CoderChatClient,\n type CoderChatClientOptions,\n type UploadedChatFile,\n} from \"../coder/client.js\";\nimport type { ChatModelConfig } from \"../coder/types.js\";\nimport {\n type PreviewShareLevel,\n resolveWorkspacePreview,\n shareWorkspacePreview,\n type WorkspaceApiConnection,\n} from \"../coder/workspaces.js\";\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport type { FileContent } from \"../files.js\";\nimport { CoderLanguageModel } from \"../model/language-model.js\";\nimport { CODER_PROVIDER_OPTIONS } from \"../model/prompt.js\";\nimport type { WorkspaceFileStore, WorkspacePlacement } from \"../workspace-files.js\";\n\ntype InnerAgent<TOOLS extends ToolSet> = ToolLoopAgent<never, TOOLS>;\n\n/** A handle to a file uploaded as a chat attachment (see {@link CoderAgent.attach}). */\nexport interface ChatAttachment extends UploadedChatFile {\n /**\n * A native AI SDK `file` part that references this upload by id. Drop it into\n * a user message's `content` to reuse the file across turns without\n * re-uploading — the agent recognizes the id and skips the upload.\n *\n * The reference travels in `providerOptions.coder.fileId`; if you persist the\n * part through a store that strips `providerOptions`, the reference is lost\n * (re-`attach()` instead of persisting the handle).\n */\n toFilePart(): FilePart;\n}\n\nfunction makeChatAttachment(file: UploadedChatFile): ChatAttachment {\n return {\n ...file,\n toFilePart: () => ({\n type: \"file\",\n // Bytes are unused: the reference travels in providerOptions.coder.fileId.\n data: \"\",\n mediaType: file.mediaType,\n ...(file.name ? { filename: file.name } : {}),\n providerOptions: { [CODER_PROVIDER_OPTIONS]: { fileId: file.id } },\n }),\n };\n}\n\n/** Default step ceiling. Each \"step\" is one client-tool round-trip; chatd caps\n * its own server-side loop at 1200 steps independently. */\nconst DEFAULT_STOP = isStepCount(64);\n\n/**\n * Bounded-cleanup defaults. An interrupted chat keeps winding down server-side\n * for a few seconds, during which archiving 409s — {@link CoderAgent.archive}\n * and `[Symbol.asyncDispose]` retry those 409s every ~`SETTLE_RETRY_DELAY_MS`\n * for up to ~`SETTLE_DEADLINE_MS` overall before giving up.\n */\nconst SETTLE_DEADLINE_MS = 15_000;\nconst SETTLE_RETRY_DELAY_MS = 1_000;\n\n/**\n * Clamps a user-supplied delay to what `AbortSignal.timeout`/`setTimeout`\n * accept: a non-negative integer no larger than the 32-bit timer cap.\n * Anything else (negative, fractional beyond flooring, `NaN`, `Infinity`)\n * falls back to the default rather than throwing at dispose time.\n */\nfunction sanitizeDelayMs(value: number | undefined, fallback: number): number {\n const MAX_TIMER_MS = 2 ** 31 - 1;\n if (value === undefined || Number.isNaN(value)) return fallback;\n if (value === Number.POSITIVE_INFINITY) return MAX_TIMER_MS;\n if (!Number.isFinite(value) || value < 0) return fallback;\n return Math.min(Math.floor(value), MAX_TIMER_MS);\n}\n\n/** Abort-aware sleep: resolves after `ms`, or rejects with the signal's reason. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Archive a chat, retrying while it is still settling. Interrupting a run does\n * not stop it instantly — chatd winds it down for a few more seconds and 409s\n * archive attempts in that window. Only those 409s are retried (other failures\n * and caller aborts rethrow immediately); once `deadlineMs` has elapsed the\n * last 409 is rethrown.\n */\nasync function archiveWhenSettled(\n client: CoderChatClient,\n chatId: string,\n opts: { deadlineMs: number; retryDelayMs: number; signal?: AbortSignal },\n): Promise<void> {\n // The deadline decides when to stop retrying; the timeout signal additionally\n // unsticks an archive request that hangs (both track the same clock).\n const timeout = AbortSignal.timeout(opts.deadlineMs);\n const signal = opts.signal ? AbortSignal.any([opts.signal, timeout]) : timeout;\n const deadline = Date.now() + opts.deadlineMs;\n for (;;) {\n try {\n await client.archiveChat(chatId, signal);\n return;\n } catch (err) {\n if (!(err instanceof CoderApiError) || err.status !== 409) throw err;\n // Stop when there is no budget left for another pause + attempt, surfacing\n // the 409 (the actionable error) rather than an opaque timeout.\n if (Date.now() + opts.retryDelayMs > deadline) throw err;\n // Pause on the caller's signal only — the deadline is enforced above.\n await sleep(opts.retryDelayMs, opts.signal);\n }\n }\n}\n\nexport interface CoderAgentSettings<TOOLS extends ToolSet = {}> {\n // --- connection (provide a client, or baseUrl + token) ---\n /**\n * A pre-built {@link CoderChatClient}, used instead of constructing one from\n * `baseUrl`/`token`. The preview helpers ({@link CoderAgent.getPreview},\n * {@link CoderAgent.sharePreview}) call non-chat endpoints and therefore\n * still need credentials — pass `baseUrl` + `token` alongside `client` to\n * enable them.\n */\n client?: CoderChatClient;\n /** Coder deployment base URL, e.g. `https://dev.coder.com`. */\n baseUrl?: string;\n /** Coder API/session token (sent as `Coder-Session-Token`). */\n token?: string;\n fetch?: CoderChatClientOptions[\"fetch\"];\n webSocketFactory?: CoderChatClientOptions[\"webSocketFactory\"];\n\n // --- Coder chat configuration ---\n /** Organization UUID that owns the chat (required). */\n organizationId: string;\n /** Model hint: UUID, `provider:model`, model id, or display-name substring. */\n model?: string;\n /** Bind the chat to a Coder workspace (enables workspace-scoped tools). */\n workspaceId?: string;\n /**\n * Adapter for writing files onto a workspace filesystem, enabling\n * {@link CoderAgent.uploadToWorkspace}. Supply one backed by a workspace\n * connection (e.g. a `@coder/ai-sdk-eve-sandbox` session).\n */\n workspaceFiles?: WorkspaceFileStore;\n /** chatd-side MCP servers to enable for this chat. */\n mcpServerIds?: string[];\n /** chatd plan mode. */\n planMode?: \"\" | \"plan\";\n /** Resume an existing chat (session) instead of creating a new one. */\n chatId?: string;\n\n // --- agent configuration ---\n /** Stable agent id (exposed as {@link CoderAgent.id}). */\n id?: string;\n /** System instructions → chatd `system_prompt`. */\n instructions?: string | SystemModelMessage | Array<SystemModelMessage>;\n /** Custom (client-executed) tools. Each should have an `execute` for scripting. */\n tools?: TOOLS;\n /**\n * Per-tool context values for tools that declare a `contextSchema`, keyed by\n * tool name (forwarded to the AI SDK loop). Required by the SDK at runtime\n * whenever a supplied tool declares contextual data.\n */\n toolsContext?: ToolLoopAgentSettings<never, TOOLS>[\"toolsContext\"];\n toolChoice?: ToolChoice<NoInfer<TOOLS>>;\n /** Stop condition(s) for the AI SDK loop. Default `isStepCount(64)`. */\n stopWhen?: StopCondition<NoInfer<TOOLS>> | Array<StopCondition<NoInfer<TOOLS>>>;\n /**\n * SDK-level retries. Default `0`: this agent owns server-side chat state, so\n * SDK retries could duplicate a turn. Override with care.\n */\n maxRetries?: number;\n /**\n * Per-segment time budget in milliseconds, applied to each model round-trip\n * (chat creation / message / tool-result submission plus the server run until\n * it settles). If a segment runs longer (e.g. the server is wedged or a\n * workspace can't be scheduled), the run is interrupted server-side and the\n * call rejects with a retryable {@link CoderChatError} (`kind: \"timeout\"`)\n * instead of hanging. A multi-step `generate()` driving client tools makes\n * several segments, so this bounds each one — to cap total wall-clock for the\n * whole call, pass `abortSignal: AbortSignal.timeout(ms)`. Unset or\n * non-positive means no limit.\n */\n requestTimeoutMs?: number;\n /**\n * Overall deadline in milliseconds for bounded cleanup: how long\n * {@link CoderAgent.archive} keeps retrying while a freshly interrupted chat\n * settles server-side, and how long `[Symbol.asyncDispose]` may take in\n * total. Default 15 000. Primarily a test/tuning knob.\n */\n settleDeadlineMs?: number;\n /**\n * Pause in milliseconds between {@link CoderAgent.archive} retries while the\n * chat settles. Default 1000. Primarily a test/tuning knob.\n */\n settleRetryDelayMs?: number;\n}\n\n/** Options for {@link CoderAgent.getPreview}. */\nexport interface WorkspacePreviewOptions {\n /** Workspace port the app listens on (4–5 digit ports parse most reliably). */\n port: number;\n /**\n * Agent that serves the port. Optional when the workspace has exactly one\n * agent; required when it has several (the error lists the candidates).\n */\n agentName?: string;\n /**\n * Protocol the app speaks *inside the workspace* on that port — `https` adds\n * the `s` suffix (`3000s--…`) so the proxy connects over TLS. This is not\n * the browser scheme, which follows the deployment's access URL.\n * Default `\"http\"`.\n */\n protocol?: \"http\" | \"https\";\n signal?: AbortSignal;\n}\n\n/** A resolved workspace preview URL (see {@link CoderAgent.getPreview}). */\nexport interface WorkspacePreview {\n /** Browser-openable subdomain app URL, e.g. `https://3000--main--dev--alice.apps.example.com`. */\n url: string;\n}\n\n/** Options for {@link CoderAgent.sharePreview}. */\nexport interface SharePreviewOptions extends WorkspacePreviewOptions {\n /**\n * Who may open the preview: `authenticated` (any logged-in user; the\n * default), `organization` (members of the workspace's organization;\n * requires a newer Coder server), or `public` (no auth at all — mind what\n * the port serves).\n */\n shareLevel?: PreviewShareLevel;\n}\n\n/** A shared workspace preview (see {@link CoderAgent.sharePreview}). */\nexport interface SharedWorkspacePreview extends WorkspacePreview {\n /** The share level now in effect for the port. */\n shareLevel: PreviewShareLevel;\n}\n\n/**\n * An AI SDK-compliant agent backed by a remote Coder `chatd` agent runtime.\n *\n * `new CoderAgent({ ... })` returns an object that implements the AI SDK\n * `Agent` interface (`generate()`/`stream()`), so it composes with the rest of\n * the AI SDK ecosystem. Internally it wraps a {@link ToolLoopAgent} whose model\n * is a {@link CoderLanguageModel}; chatd runs the actual agent loop server-side.\n *\n * One `CoderAgent` instance corresponds to one chatd chat (\"session\"): the chat\n * is created on the first `generate()`/`stream()` and reused for subsequent\n * turns. Use {@link CoderAgent.resetSession} to start a fresh chat.\n */\nexport class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOOLS> {\n readonly version = \"agent-v1\" as const;\n\n readonly #client: CoderChatClient;\n readonly #model: CoderLanguageModel;\n readonly #inner: InnerAgent<TOOLS>;\n readonly #organizationId: string;\n readonly #workspaceFiles: WorkspaceFileStore | undefined;\n readonly #workspaceId: string | undefined;\n readonly #workspaceApi: WorkspaceApiConnection | undefined;\n readonly #settleDeadlineMs: number;\n readonly #settleRetryDelayMs: number;\n\n constructor(settings: CoderAgentSettings<TOOLS>) {\n // Fail fast on an incompatible AI SDK major (see peer dependency `ai@^7`).\n assertSupportedAiVersion();\n\n if (settings.client) {\n this.#client = settings.client;\n } else if (settings.baseUrl && settings.token) {\n this.#client = new CoderChatClient({\n baseUrl: settings.baseUrl,\n token: settings.token,\n fetch: settings.fetch,\n webSocketFactory: settings.webSocketFactory,\n });\n } else {\n throw new CoderAgentError(\n \"CoderAgent requires either `client` or both `baseUrl` and `token`.\",\n );\n }\n\n this.#organizationId = settings.organizationId;\n this.#workspaceFiles = settings.workspaceFiles;\n this.#workspaceId = settings.workspaceId;\n // The preview helpers call stable v2 endpoints that CoderChatClient (chat\n // scoped) does not expose, so they need the raw credentials when given.\n this.#workspaceApi =\n settings.baseUrl && settings.token\n ? { baseUrl: settings.baseUrl, token: settings.token, fetch: settings.fetch }\n : undefined;\n // Sanitized so `AbortSignal.timeout` (which rejects non-integer, negative,\n // and non-finite delays with a RangeError) can never make disposal throw.\n this.#settleDeadlineMs = sanitizeDelayMs(settings.settleDeadlineMs, SETTLE_DEADLINE_MS);\n this.#settleRetryDelayMs = sanitizeDelayMs(settings.settleRetryDelayMs, SETTLE_RETRY_DELAY_MS);\n\n // A file written to one workspace isn't visible to a chat bound to another.\n if (\n settings.workspaceFiles &&\n settings.workspaceId &&\n settings.workspaceFiles.workspaceId !== settings.workspaceId\n ) {\n throw new CoderAgentError(\n `workspaceFiles.workspaceId (${settings.workspaceFiles.workspaceId}) does not match the ` +\n `agent's workspaceId (${settings.workspaceId}); the chat's tools would not see uploaded files.`,\n );\n }\n\n this.#model = new CoderLanguageModel({\n client: this.#client,\n organizationId: settings.organizationId,\n model: settings.model,\n workspaceId: settings.workspaceId,\n mcpServerIds: settings.mcpServerIds,\n planMode: settings.planMode,\n chatId: settings.chatId,\n requestTimeoutMs: settings.requestTimeoutMs,\n });\n\n // Whether `toolsContext` is required depends on the concrete TOOLS the\n // caller supplies (tools with a `contextSchema`), a conditional TypeScript\n // cannot resolve for a generic TOOLS — hence the cast. The field itself is\n // typed on CoderAgentSettings, so callers still get the checked shape.\n this.#inner = new ToolLoopAgent<never, TOOLS>({\n model: this.#model,\n id: settings.id,\n instructions: settings.instructions,\n tools: settings.tools,\n toolsContext: settings.toolsContext,\n toolChoice: settings.toolChoice,\n stopWhen: settings.stopWhen ?? DEFAULT_STOP,\n maxRetries: settings.maxRetries ?? 0,\n } as ToolLoopAgentSettings<never, TOOLS>);\n }\n\n get id(): string | undefined {\n return this.#inner.id;\n }\n\n get tools(): TOOLS {\n return this.#inner.tools;\n }\n\n /** The underlying chatd client. */\n get client(): CoderChatClient {\n return this.#client;\n }\n\n /** The current chatd chat id, once a turn has started. */\n get chatId(): string | undefined {\n return this.#model.chatId;\n }\n\n generate(\n options: Parameters<InnerAgent<TOOLS>[\"generate\"]>[0],\n ): ReturnType<InnerAgent<TOOLS>[\"generate\"]> {\n return this.#inner.generate(options);\n }\n\n stream(\n options: Parameters<InnerAgent<TOOLS>[\"stream\"]>[0],\n ): ReturnType<InnerAgent<TOOLS>[\"stream\"]> {\n return this.#inner.stream(options);\n }\n\n // --- session helpers ------------------------------------------------------\n\n /**\n * List the model configs available on the deployment. Use this to discover\n * valid values for the `model` hint instead of guessing ids — match on\n * `id`, `provider`/`model`, or `display_name`.\n */\n listModels(signal?: AbortSignal): Promise<ChatModelConfig[]> {\n return this.#client.listModelConfigs(signal);\n }\n\n /** Start a fresh chatd chat on the next turn. */\n resetSession(): void {\n this.#model.resetSession();\n }\n\n /**\n * Interrupt the in-flight generation, if any. Resolves as soon as the server\n * acknowledges the interrupt — the run keeps winding down asynchronously for\n * a few seconds afterwards (see {@link CoderAgent.archive}).\n */\n async interrupt(opts?: { signal?: AbortSignal }): Promise<void> {\n const id = this.#model.chatId;\n if (id) await this.#client.interruptChat(id, opts?.signal);\n }\n\n /**\n * Archive the underlying chat (safe cleanup; hides it from listings).\n *\n * A freshly interrupted/settled chat can keep winding down server-side for a\n * few seconds, during which the server rejects archiving with a 409. Those\n * 409s are retried (~1s apart, ~15s overall — see `settleDeadlineMs` /\n * `settleRetryDelayMs`) and the last one is rethrown if the chat never\n * settles; any other failure, including a caller abort, rethrows immediately.\n */\n async archive(opts?: { signal?: AbortSignal }): Promise<void> {\n const id = this.#model.chatId;\n if (!id) return;\n await archiveWhenSettled(this.#client, id, {\n deadlineMs: this.#settleDeadlineMs,\n retryDelayMs: this.#settleRetryDelayMs,\n signal: opts?.signal,\n });\n }\n\n /**\n * Clean up the chat when the agent leaves an `await using` scope, so cleanup\n * rides scope exit instead of a separate call you have to remember in a\n * `finally`. Interrupts any in-flight server run, then archives the chat\n * (retrying while the interrupted run settles). Best-effort and bounded\n * (~15s overall): disposal errors are swallowed after the bounded attempts\n * so they can't mask the scope's own error — call {@link CoderAgent.archive}\n * directly when you need guaranteed cleanup.\n *\n * @example\n * ```ts\n * await using agent = new CoderAgent({ ... });\n * const { text } = await agent.generate({ prompt: \"…\" });\n * // agent.interrupt() + agent.archive() run automatically here.\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n // archive() only soft-hides the chat; it does not stop a generation. If the\n // scope exits mid-turn (e.g. generate() threw, or an early return), interrupt\n // first so chatd stops generating and releases the workspace, then archive.\n // One shared timeout bounds both calls so scope exit can never hang.\n const deadline = AbortSignal.timeout(this.#settleDeadlineMs);\n try {\n await this.interrupt({ signal: deadline });\n } catch {\n /* best-effort cleanup */\n }\n try {\n await this.archive({ signal: deadline });\n } catch {\n /* best-effort cleanup */\n }\n }\n\n // --- files ----------------------------------------------------------------\n\n /**\n * Upload a file as a chat attachment — content for the model to *read* (a PDF,\n * image, CSV, …). Returns a handle you can reference from a later turn via\n * {@link ChatAttachment.toFilePart} (no re-upload). Validated against the chat\n * allowlist and 10 MiB cap up front.\n *\n * You usually don't need this: a native AI SDK `file` part dropped into a\n * message's `content` is uploaded transparently. Reach for `attach()` to\n * pre-upload, to reuse one file across turns, or to validate before sending.\n *\n * For large or non-allowlisted files (zips, datasets, binaries) — material for\n * the agent to *operate on* — use {@link CoderAgent.uploadToWorkspace}.\n */\n async attach(file: ChatFileInput, signal?: AbortSignal): Promise<ChatAttachment> {\n const uploaded = await this.#client.uploadChatFile(this.#organizationId, file, signal);\n return makeChatAttachment(uploaded);\n }\n\n /**\n * Write a file onto the bound workspace's filesystem for the agent to operate\n * on (extract, build, read with its tools). Requires a {@link WorkspaceFileStore}\n * via the `workspaceFiles` setting. The file is written as-is — no unpacking;\n * instruct the agent to `unzip`/`tar -x`, or extract over your own connection.\n *\n * The chat should be bound to the same workspace (`workspaceId`) so the agent's\n * server-side tools can see the file.\n */\n async uploadToWorkspace(file: {\n content: FileContent;\n /** Destination path inside the workspace (the adapter may resolve relatives). */\n path: string;\n signal?: AbortSignal;\n }): Promise<WorkspacePlacement> {\n if (!this.#workspaceFiles) {\n throw new CoderAgentError(\n \"uploadToWorkspace requires a `workspaceFiles` adapter. Construct the agent with \" +\n \"`workspaceFiles` (e.g. backed by a @coder/ai-sdk-eve-sandbox session) to enable it.\",\n );\n }\n const { path } = await this.#workspaceFiles.write({\n content: file.content,\n path: file.path,\n signal: file.signal,\n });\n return { workspaceId: this.#workspaceFiles.workspaceId, path };\n }\n\n // --- previews ---------------------------------------------------------------\n\n /** The v2 connection + workspace id the preview helpers need, or a clear error. */\n #previewContext(method: string): { conn: WorkspaceApiConnection; workspaceId: string } {\n if (!this.#workspaceId) {\n throw new CoderAgentError(\n `${method}() requires the agent to be bound to a workspace — construct the CoderAgent ` +\n `with the \\`workspaceId\\` setting.`,\n );\n }\n if (!this.#workspaceApi) {\n throw new CoderAgentError(\n `${method}() needs the deployment's REST credentials — construct the CoderAgent with ` +\n `\\`baseUrl\\` and \\`token\\` (they can accompany \\`client\\`).`,\n );\n }\n return { conn: this.#workspaceApi, workspaceId: this.#workspaceId };\n }\n\n /**\n * Resolve the browser URL where a port on the bound workspace can be\n * previewed, e.g. `https://3000--main--dev--alice.apps.example.com`.\n * Composed from stable v2 endpoints (workspace lookup + wildcard app host),\n * so it works against old Coder servers. Requires the `workspaceId` setting,\n * `baseUrl`/`token` credentials, and a deployment with a wildcard access URL\n * configured.\n *\n * The URL honors the port's current share level — private to the workspace\n * owner unless shared (see {@link CoderAgent.sharePreview}).\n */\n async getPreview(opts: WorkspacePreviewOptions): Promise<WorkspacePreview> {\n const { conn, workspaceId } = this.#previewContext(\"getPreview\");\n const { url } = await resolveWorkspacePreview(\n conn,\n { workspaceId, port: opts.port, agentName: opts.agentName, protocol: opts.protocol },\n opts.signal,\n );\n return { url };\n }\n\n /**\n * Share a port on the bound workspace and return its preview URL. Upserts\n * the port's share level — `authenticated` by default — so teammates (or,\n * with `public`, anyone) can open what the agent is serving; re-invoking\n * updates the level in place. On Coder servers that predate port sharing\n * (< v2.9) this fails with a 404 {@link CoderApiError} saying so.\n */\n async sharePreview(opts: SharePreviewOptions): Promise<SharedWorkspacePreview> {\n const { conn, workspaceId } = this.#previewContext(\"sharePreview\");\n return shareWorkspacePreview(\n conn,\n {\n workspaceId,\n port: opts.port,\n agentName: opts.agentName,\n protocol: opts.protocol,\n shareLevel: opts.shareLevel,\n },\n opts.signal,\n );\n }\n}\n","import type { ChatErrorPayload } from \"./coder/types.js\";\n\n/** Base error for all `@coder/ai-sdk-agent` failures. */\nexport class CoderAgentError extends Error {\n override name = \"CoderAgentError\";\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n }\n}\n\n/** An HTTP request to the Coder API failed. */\nexport class CoderApiError extends CoderAgentError {\n override name = \"CoderApiError\";\n readonly status: number;\n readonly detail: string | undefined;\n readonly method: string;\n readonly path: string;\n constructor(args: {\n status: number;\n method: string;\n path: string;\n message: string;\n detail?: string;\n }) {\n super(\n `Coder API ${args.method} ${args.path} failed (${args.status}): ${args.message}` +\n (args.detail ? ` — ${args.detail}` : \"\"),\n );\n this.status = args.status;\n this.detail = args.detail;\n this.method = args.method;\n this.path = args.path;\n }\n}\n\n/** A chat generation ended in an error status. */\nexport class CoderChatError extends CoderAgentError {\n override name = \"CoderChatError\";\n readonly kind: string | undefined;\n readonly provider: string | undefined;\n readonly retryable: boolean;\n readonly statusCode: number | undefined;\n constructor(payload: ChatErrorPayload) {\n super(payload.detail ? `${payload.message}: ${payload.detail}` : payload.message);\n this.kind = payload.kind;\n this.provider = payload.provider;\n this.retryable = payload.retryable ?? false;\n this.statusCode = payload.status_code;\n }\n}\n","import { CoderAgentError } from \"./errors.js\";\n\n/** The `ai` peer-dependency major this package is built against (`ai@^7`). */\nconst SUPPORTED_AI_MAJOR = 7;\n\n/**\n * Decides whether a resolved `ai` package version is supported. Returns the\n * error message to raise for a known-incompatible major, or `undefined` to\n * proceed. Unparseable strings also return `undefined`: the guard fails open\n * rather than inventing a failure on exotic version formats.\n */\nexport function unsupportedAiVersionMessage(version: string): string | undefined {\n const major = Number(/^v?(\\d+)(?:[.+-]|$)/.exec(version.trim())?.[1]);\n if (!Number.isSafeInteger(major) || major === SUPPORTED_AI_MAJOR) return undefined;\n return (\n `@coder/ai-sdk-agent supports the \\`ai\\` package v${SUPPORTED_AI_MAJOR} only ` +\n `(peer dependency \\`ai@^${SUPPORTED_AI_MAJOR}\\`), but ai@${version} is installed. ` +\n `Other majors fail at runtime in confusing ways (brand-check TypeErrors, silently ` +\n `empty streams). Install a compatible version, e.g. \\`pnpm add ai@^${SUPPORTED_AI_MAJOR}\\`.`\n );\n}\n\n/**\n * Best-effort resolution of the installed `ai` package's version via\n * `createRequire(import.meta.url)(\"ai/package.json\")` (both v6 and v7 export\n * `./package.json`). Returns `undefined` whenever resolution isn't possible —\n * non-Node runtime, bundle without module resolution, `ai` not installed, … —\n * because the guard must never introduce a failure of its own. `node:module`\n * is reached through `process.getBuiltinModule` (Node ≥ 20.16) instead of a\n * static import, which would crash non-Node runtimes at module-load time.\n */\nfunction resolveInstalledAiVersion(): string | undefined {\n try {\n const nodeModule =\n typeof process !== \"undefined\" && typeof process.getBuiltinModule === \"function\"\n ? process.getBuiltinModule(\"node:module\")\n : undefined;\n const requireFromHere = nodeModule?.createRequire(import.meta.url);\n const pkg = requireFromHere?.(\"ai/package.json\") as { version?: unknown } | undefined;\n return typeof pkg?.version === \"string\" ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Memoized verdict: `undefined` = not yet computed, `null` = supported (or\n * unresolvable), a string = the message to throw on every construction.\n */\nlet cachedMessage: string | null | undefined;\n\n/**\n * Asserts that the installed `ai` major matches this package's peer range\n * (`ai@^7`). Called from the `CoderAgent`/`CoderLanguageModel` constructors so\n * an incompatible AI SDK fails fast with an actionable error instead of the\n * cryptic failures it produces mid-generation. Fails open when the installed\n * version cannot be resolved. The verdict is memoized across calls.\n *\n * @param resolveVersion Test seam; production callers use the default.\n */\nexport function assertSupportedAiVersion(\n resolveVersion: () => string | undefined = resolveInstalledAiVersion,\n): void {\n if (cachedMessage === undefined) {\n const version = resolveVersion();\n cachedMessage = version === undefined ? null : (unsupportedAiVersionMessage(version) ?? null);\n }\n if (cachedMessage !== null) throw new CoderAgentError(cachedMessage);\n}\n\n/** Clears the memoized verdict so resolution runs again (unit-test hook). */\nexport function resetAiVersionCheckForTests(): void {\n cachedMessage = undefined;\n}\n","import type { SharedV4FileData } from \"@ai-sdk/provider\";\nimport { CoderAgentError } from \"./errors.js\";\n\n/**\n * Bytes to upload, in any convenient shape.\n *\n * `Blob`/`File` is the recommended form: it is lazy (not forced fully into\n * memory), carries its own size (so the 10 MiB chat cap can fail fast and a\n * `Content-Length` can be sent), is re-readable (so a failed upload can be\n * retried), and works both in Node (`fs.openAsBlob(\"./x.pdf\")`) and the browser\n * (an `<input type=file>` yields a `File`). A `ReadableStream` is accepted for\n * genuinely unbounded sources, but its size is unknown up front — the chat size\n * cap can then only be enforced server-side.\n */\nexport type FileContent = Blob | ReadableStream<Uint8Array> | Uint8Array | ArrayBuffer;\n\n/** A {@link FileContent} normalized for upload: a fetch-ready body plus derived metadata. */\nexport interface ResolvedFile {\n /**\n * Body to hand to `fetch`. Raw bytes are wrapped in a `Blob` so the body is\n * always a clean `BodyInit` (and bytes get a `Content-Length` for free).\n */\n body: Blob | ReadableStream<Uint8Array>;\n /** Resolved IANA media type. */\n mediaType: string;\n /** Original filename, if known. */\n name: string | undefined;\n /** Byte length, if known up front (undefined for streams). */\n size: number | undefined;\n}\n\nfunction isBlob(v: unknown): v is Blob {\n return typeof Blob !== \"undefined\" && v instanceof Blob;\n}\nfunction isReadableStream(v: unknown): v is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== \"undefined\" && v instanceof ReadableStream;\n}\n\n/**\n * Normalize {@link FileContent} into a fetch-ready body and derive its media\n * type, name, and (when known) size. `Blob`/`File` supply their own\n * `type`/`name`/`size`; for raw bytes or a stream, `mediaType` must be provided\n * by the caller. Explicit `opts.mediaType`/`opts.name` always win.\n */\nexport function resolveFileContent(\n content: FileContent,\n opts?: { mediaType?: string; name?: string },\n): ResolvedFile {\n let mediaType = opts?.mediaType;\n let name = opts?.name;\n let body: ResolvedFile[\"body\"];\n let size: number | undefined;\n\n if (isBlob(content)) {\n body = content;\n size = content.size;\n if (!mediaType && content.type) mediaType = content.type;\n // `File` extends `Blob` with a `name`; read it structurally so we don't\n // depend on the `File` global type existing.\n const maybeName = (content as { name?: unknown }).name;\n if (!name && typeof maybeName === \"string\") name = maybeName;\n } else if (isReadableStream(content)) {\n body = content;\n size = undefined;\n } else if (content instanceof Uint8Array || content instanceof ArrayBuffer) {\n size = content.byteLength;\n body = new Blob([content as BlobPart]);\n } else {\n throw new CoderAgentError(\n \"Unsupported file content: expected a Blob/File, ReadableStream, Uint8Array, or ArrayBuffer.\",\n );\n }\n\n if (!mediaType) {\n throw new CoderAgentError(\n \"Could not determine the file's media type. Pass `mediaType`, or provide a Blob/File that carries its own `type`.\",\n );\n }\n\n return { body, mediaType, name, size };\n}\n\n/** Decode a base64 string to bytes (Node `Buffer` or browser `atob`). */\nfunction base64ToBytes(b64: string): Uint8Array {\n // `Buffer` is already a `Uint8Array`; return it directly (no extra copy).\n if (typeof Buffer !== \"undefined\") return Buffer.from(b64, \"base64\");\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\n/**\n * Convert an AI SDK file-part `data` value (a tagged {@link SharedV4FileData}\n * union) into {@link FileContent} for upload. The provider receives inline\n * bytes, a base64 string, or inline text. URLs are not expected: this model\n * declares no `supportedUrls`, so the SDK downloads them to bytes before\n * calling us — a URL (or an opaque provider reference) here is therefore an\n * error.\n */\nexport function dataContentToFileContent(data: SharedV4FileData): FileContent {\n switch (data.type) {\n case \"data\":\n return data.data instanceof Uint8Array ? data.data : base64ToBytes(data.data);\n case \"text\":\n return new TextEncoder().encode(data.text);\n default:\n throw new CoderAgentError(\n `File parts referencing a ${data.type === \"url\" ? \"URL\" : \"provider reference\"} are not supported; provide the file bytes instead.`,\n );\n }\n}\n","/**\n * Wire types for Coder's experimental `chatd` chat API\n * (`/api/experimental/chats`). These mirror the Go types in\n * `coder/coder`'s `codersdk/chats.go` (JSON shapes), restricted to the subset\n * this client uses. Field names use snake_case to match the wire format.\n *\n * NOTE: The chatd API is experimental and may change between Coder releases.\n */\n\n// ---------------------------------------------------------------------------\n// Input (client -> server)\n// ---------------------------------------------------------------------------\n\nexport type ChatInputPartType = \"text\" | \"file\" | \"file-reference\";\n\nexport interface ChatInputPart {\n type: ChatInputPartType;\n text?: string;\n file_id?: string;\n file_name?: string;\n start_line?: number;\n end_line?: number;\n content?: string;\n}\n\n/**\n * Media types accepted by the chat-file upload endpoint\n * (`POST /api/experimental/chats/files`). The server allowlist is intentionally\n * narrow — anything else (zip, tarball, binaries, …) is not a chat attachment\n * and belongs on the workspace filesystem instead. Mirrors\n * `codersdk.AllChatAttachmentMediaTypes`.\n */\nexport type ChatAttachmentMediaType =\n | \"application/json\"\n | \"application/pdf\"\n | \"image/gif\"\n | \"image/jpeg\"\n | \"image/png\"\n | \"image/webp\"\n | \"text/csv\"\n | \"text/markdown\"\n | \"text/plain\";\n\n/** The chat-attachment media-type allowlist, as a runtime-checkable set. */\nexport const CHAT_ATTACHMENT_MEDIA_TYPES: ReadonlySet<string> = new Set<ChatAttachmentMediaType>([\n \"application/json\",\n \"application/pdf\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"text/csv\",\n \"text/markdown\",\n \"text/plain\",\n]);\n\n/** Per-file size cap enforced by the chat-file upload endpoint (10 MiB). */\nexport const MAX_CHAT_FILE_SIZE_BYTES = 10 * 1024 * 1024;\n\nexport type ChatClientType = \"ui\" | \"api\";\nexport type ChatPlanMode = \"\" | \"plan\";\nexport type ChatBusyBehavior = \"queue\" | \"interrupt\";\n\n/**\n * A client-executed (\"dynamic\") tool definition. chatd never executes these;\n * when the model calls one, the chat enters `requires_action` and the client\n * must execute it and submit results via {@link SubmitToolResultsRequest}.\n *\n * NOTE: the JSON key is `input_schema` (snake_case), per chatd.\n */\nexport interface DynamicTool {\n name: string;\n description?: string;\n /** A JSON Schema object describing the tool input. */\n input_schema: unknown;\n}\n\nexport interface CreateChatRequest {\n organization_id: string;\n content: ChatInputPart[];\n system_prompt?: string;\n workspace_id?: string;\n model_config_id?: string;\n mcp_server_ids?: string[];\n labels?: Record<string, string>;\n unsafe_dynamic_tools?: DynamicTool[];\n plan_mode?: ChatPlanMode;\n client_type?: ChatClientType;\n}\n\nexport interface CreateChatMessageRequest {\n content: ChatInputPart[];\n model_config_id?: string;\n mcp_server_ids?: string[];\n busy_behavior?: ChatBusyBehavior;\n plan_mode?: ChatPlanMode;\n}\n\n/** Result of a single client-executed tool call. */\nexport interface ToolResult {\n tool_call_id: string;\n /** JSON-serializable tool output. */\n output: unknown;\n is_error?: boolean;\n}\n\nexport interface SubmitToolResultsRequest {\n results: ToolResult[];\n}\n\nexport interface UpdateChatRequest {\n title?: string;\n archived?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Output (server -> client)\n// ---------------------------------------------------------------------------\n\nexport type ChatStatus =\n | \"waiting\"\n | \"pending\"\n | \"running\"\n | \"paused\"\n | \"completed\"\n | \"error\"\n | \"requires_action\"\n | \"interrupting\";\n\nexport type ChatMessageRole = \"system\" | \"user\" | \"assistant\" | \"tool\";\n\nexport type ChatMessagePartType =\n | \"text\"\n | \"reasoning\"\n | \"tool-call\"\n | \"tool-result\"\n | \"source\"\n | \"file\"\n | \"file-reference\"\n | \"context-file\"\n | \"skill\";\n\n/**\n * A part of a chat message. chatd models this as a single flat struct with a\n * `type` discriminator and many optional fields (see `codersdk.ChatMessagePart`),\n * so we mirror that shape rather than a strict discriminated union.\n */\nexport interface ChatMessagePart {\n type: ChatMessagePartType;\n\n // text / reasoning\n text?: string;\n signature?: string;\n\n // tool-call / tool-result\n tool_call_id?: string;\n tool_name?: string;\n mcp_server_config_id?: string | null;\n /** tool-call: complete arguments (JSON value). */\n args?: unknown;\n /** tool-call: incremental argument text chunk during streaming. */\n args_delta?: string;\n parsed_commands?: string[][];\n /** tool-result: complete result (JSON value). */\n result?: unknown;\n /** tool-result: incremental result text chunk during streaming. */\n result_delta?: string;\n result_reset?: boolean;\n is_error?: boolean;\n is_media?: boolean;\n provider_executed?: boolean;\n created_at?: string;\n completed_at?: string;\n\n // source\n source_id?: string;\n url?: string;\n title?: string;\n\n // file\n media_type?: string;\n name?: string;\n file_id?: string | null;\n\n // file-reference\n file_name?: string;\n start_line?: number;\n end_line?: number;\n content?: string;\n\n // skill\n skill_name?: string;\n skill_description?: string;\n}\n\nexport interface ChatMessageUsage {\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n reasoning_tokens?: number;\n cache_creation_tokens?: number;\n cache_read_tokens?: number;\n context_limit?: number;\n /**\n * Cost in micro-USD, when the server prices usage. Optional: servers that\n * don't price usage (or predate cost reporting) omit it.\n */\n total_cost_micros?: number;\n /** Model runtime in milliseconds, when the server reports it. */\n total_runtime_ms?: number;\n}\n\nexport interface ChatMessage {\n id: number;\n chat_id: string;\n created_by?: string;\n model_config_id?: string;\n created_at: string;\n role: ChatMessageRole;\n content?: ChatMessagePart[];\n usage?: ChatMessageUsage;\n}\n\nexport interface ChatErrorPayload {\n message: string;\n detail?: string;\n kind?: string;\n provider?: string;\n retryable?: boolean;\n status_code?: number;\n}\n\nexport interface Chat {\n id: string;\n organization_id: string;\n owner_id: string;\n owner_username?: string;\n workspace_id?: string | null;\n agent_id?: string | null;\n parent_chat_id?: string | null;\n root_chat_id?: string | null;\n last_model_config_id?: string;\n title: string;\n status: ChatStatus;\n plan_mode?: ChatPlanMode;\n last_error?: ChatErrorPayload | null;\n created_at: string;\n updated_at: string;\n archived: boolean;\n mcp_server_ids?: string[];\n client_type?: ChatClientType;\n}\n\nexport interface ChatQueuedMessage {\n id: number;\n chat_id: string;\n content: ChatMessagePart[];\n created_at: string;\n}\n\nexport interface CreateChatMessageResponse {\n message?: ChatMessage;\n queued_message?: ChatQueuedMessage;\n queued: boolean;\n warnings?: string[];\n}\n\nexport interface ChatMessagesResponse {\n messages: ChatMessage[];\n queued_messages: ChatQueuedMessage[];\n has_more: boolean;\n}\n\n/** Response from `POST /api/experimental/chats/files`: the uploaded file's id. */\nexport interface UploadChatFileResponse {\n id: string;\n}\n\n/**\n * A configured model (`GET /api/experimental/chats/model-configs`). Depending\n * on the server version and how a config was created, `provider`, `model`, and\n * `display_name` may be absent on individual entries, so treat them as\n * optional and guard reads.\n */\nexport interface ChatModelConfig {\n id: string;\n provider?: string;\n ai_provider_id?: string;\n model?: string;\n display_name?: string;\n enabled?: boolean;\n is_default?: boolean;\n context_limit?: number;\n compression_threshold?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Stream events (WebSocket `/stream`)\n// ---------------------------------------------------------------------------\n\nexport type ChatStreamEventType =\n | \"message_part\"\n | \"message\"\n | \"status\"\n | \"error\"\n | \"queue_update\"\n | \"retry\"\n | \"action_required\"\n | \"preview_reset\"\n | \"history_reset\";\n\nexport interface ChatStreamMessagePart {\n role?: ChatMessageRole;\n part: ChatMessagePart;\n history_version?: number;\n generation_attempt?: number;\n seq?: number;\n}\n\nexport interface ChatStreamStatus {\n status: ChatStatus;\n}\n\nexport interface ChatStreamToolCall {\n tool_call_id: string;\n tool_name: string;\n /** JSON string of the tool-call arguments. */\n args: string;\n}\n\nexport interface ChatStreamActionRequired {\n tool_calls: ChatStreamToolCall[];\n}\n\nexport interface ChatStreamRetry {\n attempt: number;\n delay_ms: number;\n error: string;\n kind?: string;\n provider?: string;\n status_code?: number;\n retrying_at: string;\n}\n\nexport interface ChatStreamEvent {\n type: ChatStreamEventType;\n chat_id: string;\n message?: ChatMessage;\n message_part?: ChatStreamMessagePart;\n status?: ChatStreamStatus;\n error?: ChatErrorPayload;\n retry?: ChatStreamRetry;\n queued_messages?: ChatQueuedMessage[];\n action_required?: ChatStreamActionRequired;\n}\n\n/** Terminal statuses: the chat has stopped generating for this turn. */\nexport const TERMINAL_STATUSES: ReadonlySet<ChatStatus> = new Set<ChatStatus>([\n \"waiting\",\n \"completed\",\n \"error\",\n \"requires_action\",\n]);\n\n// ---------------------------------------------------------------------------\n// Watch events (WebSocket `/watch`)\n// ---------------------------------------------------------------------------\n\n/** Kinds of chat lifecycle events emitted on the per-user watch socket. */\nexport type ChatWatchEventKind =\n | \"status_change\"\n | \"summary_change\"\n | \"title_change\"\n | \"created\"\n | \"deleted\"\n | \"diff_status_change\"\n | \"action_required\"\n | \"context_dirty\";\n\n/**\n * An event from `GET /api/experimental/chats/watch`, which covers every chat\n * visible to the authenticated user (not a single chat). Mirrors\n * `codersdk.ChatWatchEvent`.\n */\nexport interface ChatWatchEvent {\n kind: ChatWatchEventKind;\n chat: Chat;\n /** Pending client-executed tool calls; sent with `action_required` events. */\n tool_calls?: ChatStreamToolCall[];\n}\n","import NodeWebSocket from \"ws\";\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport type { ChatStreamEvent, ChatWatchEvent } from \"./types.js\";\n\n/**\n * A minimal WebSocket factory abstraction so the stream reader can run on\n * Node (via the `ws` package, with header auth) or in environments that\n * provide a global `WebSocket` (browsers, where auth must go in the query\n * string). Node is the default target.\n */\nexport interface WebSocketLike {\n send(data: string): void;\n close(code?: number): void;\n addEventListener(type: \"open\", cb: () => void): void;\n addEventListener(type: \"message\", cb: (ev: { data: unknown }) => void): void;\n addEventListener(type: \"error\", cb: (ev: unknown) => void): void;\n addEventListener(type: \"close\", cb: (ev: { code?: number; reason?: string }) => void): void;\n removeEventListener(type: \"open\", cb: () => void): void;\n removeEventListener(type: \"message\", cb: (ev: { data: unknown }) => void): void;\n removeEventListener(type: \"error\", cb: (ev: unknown) => void): void;\n removeEventListener(type: \"close\", cb: (ev: { code?: number; reason?: string }) => void): void;\n}\n\nexport type WebSocketFactory = (\n url: string,\n protocols: { headers: Record<string, string> },\n) => WebSocketLike;\n\nconst defaultFactory: WebSocketFactory = (url, { headers }) => {\n // `ws` accepts custom handshake headers, which lets us authenticate with the\n // `Coder-Session-Token` header instead of leaking the token into the URL.\n return new NodeWebSocket(url, { headers }) as unknown as WebSocketLike;\n};\n\nfunction httpToWs(baseUrl: string): string {\n if (baseUrl.startsWith(\"https://\")) return `wss://${baseUrl.slice(\"https://\".length)}`;\n if (baseUrl.startsWith(\"http://\")) return `ws://${baseUrl.slice(\"http://\".length)}`;\n return baseUrl;\n}\n\nexport interface StreamChatEventsOptions {\n baseUrl: string;\n token: string;\n chatId: string;\n /** Only stream events for messages with id greater than this. */\n afterId?: number;\n signal?: AbortSignal;\n webSocketFactory?: WebSocketFactory;\n}\n\n/**\n * Opens the chatd `/stream` WebSocket and yields decoded {@link ChatStreamEvent}s\n * as an async iterable. Each text frame from chatd is a JSON array of events;\n * we flatten them into a single stream.\n *\n * The chat stream is a live subscription that stays open after a turn settles,\n * so callers should `break` out of the loop once they observe a terminal status\n * — that triggers generator cleanup, which closes the socket.\n */\nexport async function* streamChatEvents(\n options: StreamChatEventsOptions,\n): AsyncGenerator<ChatStreamEvent, void, void> {\n const { baseUrl, token, chatId, afterId, signal } = options;\n const factory = options.webSocketFactory ?? defaultFactory;\n\n const wsBase = httpToWs(baseUrl);\n const query = afterId !== undefined ? `?after_id=${afterId}` : \"\";\n const url = `${wsBase}/api/experimental/chats/${chatId}/stream${query}`;\n\n const queue: ChatStreamEvent[] = [];\n let resolveNext: (() => void) | undefined;\n let finished = false;\n let failure: Error | undefined;\n\n const wake = () => {\n resolveNext?.();\n resolveNext = undefined;\n };\n\n const ws = factory(url, { headers: { \"Coder-Session-Token\": token } });\n\n const onAbort = () => {\n finished = true;\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n wake();\n };\n let abortListenerAdded = false;\n if (signal) {\n if (signal.aborted) onAbort();\n else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n abortListenerAdded = true;\n }\n }\n\n const onMessage = (ev: { data: unknown }): void => {\n if (finished) return;\n let batch: unknown;\n try {\n const data = typeof ev.data === \"string\" ? ev.data : String(ev.data);\n batch = JSON.parse(data);\n } catch (err) {\n failure = new CoderAgentError(\"failed to parse chat stream frame\", { cause: err });\n finished = true;\n wake();\n return;\n }\n if (Array.isArray(batch)) {\n for (const e of batch) queue.push(e as ChatStreamEvent);\n } else if (batch && typeof batch === \"object\") {\n queue.push(batch as ChatStreamEvent);\n }\n wake();\n };\n const onError = (ev: unknown): void => {\n if (finished) return;\n const message =\n ev && typeof ev === \"object\" && \"message\" in ev\n ? String((ev as { message: unknown }).message)\n : \"chat stream socket error\";\n failure = new CoderAgentError(message);\n finished = true;\n wake();\n };\n const onClose = (): void => {\n finished = true;\n wake();\n };\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n try {\n while (true) {\n while (queue.length > 0) {\n const next = queue.shift() as ChatStreamEvent;\n yield next;\n }\n if (failure) throw failure;\n if (finished) return;\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n }\n } finally {\n finished = true;\n if (signal && abortListenerAdded) signal.removeEventListener(\"abort\", onAbort);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n }\n}\n\nconst WATCH_PATH = \"/api/experimental/chats/watch\";\nconst WATCH_BACKOFF_INITIAL_MS = 1_000;\nconst WATCH_BACKOFF_CAP_MS = 30_000;\n\n/** Abort-aware sleep; resolves early (without throwing) when the signal aborts. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n // The timer stays ref'd on purpose: an actively consumed watch loop must\n // keep the process alive through a reconnect delay (abort clears it).\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Extract the HTTP status from a rejected WebSocket upgrade. The `ws` package\n * reports handshake failures as an error event whose message is\n * `Unexpected server response: <status>`; other factories that follow the\n * same convention are recognized too.\n */\nfunction upgradeStatus(message: string): number | undefined {\n const match = /unexpected server response: (\\d{3})$/i.exec(message.trim());\n return match ? Number(match[1]) : undefined;\n}\n\nexport interface WatchChatEventsOptions {\n baseUrl: string;\n token: string;\n signal?: AbortSignal;\n webSocketFactory?: WebSocketFactory;\n}\n\n/**\n * Opens the chatd `/watch` WebSocket — lifecycle events for every chat visible\n * to the authenticated user — and yields decoded {@link ChatWatchEvent}s as an\n * async iterable.\n *\n * Unlike the per-chat `/stream`, this is a long-lived subscription, so dropped\n * connections are redialed automatically with exponential backoff (1s doubling\n * to a 30s cap, reset once an event arrives). It ends in exactly three ways:\n * `signal` aborting, the consumer ending iteration (`break` from `for await`,\n * `iterator.return()`) — both tear the socket down promptly even while a read\n * is pending — or the server rejecting the upgrade with a 4xx — bad/expired\n * token, or an older Coder server without the endpoint (404) — which throws a\n * terminal {@link CoderApiError} instead of retrying forever.\n */\nexport function watchChatEvents(\n options: WatchChatEventsOptions,\n): AsyncGenerator<ChatWatchEvent, void, void> {\n // Cancellation plumbing: async-generator `return()`/`throw()` queue behind a\n // pending `next()`, and on this rarely-eventing stream a pending read is the\n // steady state — a bare `return()` would otherwise hang (socket open) until\n // the next server event. The wrapper aborts an internal controller first,\n // which wakes the pending read, then delegates.\n const controller = new AbortController();\n const external = options.signal;\n const chain = (): void => controller.abort(external?.reason);\n if (external) {\n if (external.aborted) chain();\n else external.addEventListener(\"abort\", chain, { once: true });\n }\n const inner = watchChatEventsLoop({ ...options, signal: controller.signal });\n const detach = (): void => external?.removeEventListener(\"abort\", chain);\n const finish = async (): Promise<void> => {\n controller.abort();\n detach();\n try {\n await inner.return();\n } catch {\n /* the loop's own teardown errors are not the caller's problem */\n }\n };\n return {\n // Detach the signal chain once the loop settles for good (done, or the\n // terminal 4xx rejection) — the iteration protocol never calls return()\n // on a failed iterator, and the {once} listener would otherwise pile up\n // on a long-lived signal across re-created watchers.\n async next(): Promise<IteratorResult<ChatWatchEvent, void>> {\n try {\n const result = await inner.next();\n if (result.done) detach();\n return result;\n } catch (err) {\n detach();\n throw err;\n }\n },\n async return(): Promise<IteratorResult<ChatWatchEvent, void>> {\n await finish();\n return { done: true, value: undefined };\n },\n async throw(err: unknown): Promise<IteratorResult<ChatWatchEvent, void>> {\n await finish();\n throw err;\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n // Native async generators are async-disposable; the wrapper must be too,\n // so `await using events = client.watchChats(…)` keeps tearing down.\n async [Symbol.asyncDispose](): Promise<void> {\n await finish();\n },\n } as AsyncGenerator<ChatWatchEvent, void, void>;\n}\n\nasync function* watchChatEventsLoop(\n options: WatchChatEventsOptions,\n): AsyncGenerator<ChatWatchEvent, void, void> {\n const { baseUrl, token, signal } = options;\n const factory = options.webSocketFactory ?? defaultFactory;\n const url = `${httpToWs(baseUrl)}${WATCH_PATH}`;\n\n let backoffMs = WATCH_BACKOFF_INITIAL_MS;\n while (!signal?.aborted) {\n // One connection attempt per iteration; the queue/wake plumbing mirrors\n // streamChatEvents above.\n const queue: ChatWatchEvent[] = [];\n let resolveNext: (() => void) | undefined;\n let finished = false;\n let failure: Error | undefined;\n\n const wake = () => {\n resolveNext?.();\n resolveNext = undefined;\n };\n\n const ws = factory(url, { headers: { \"Coder-Session-Token\": token } });\n\n const onAbort = () => {\n finished = true;\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n wake();\n };\n let abortListenerAdded = false;\n if (signal) {\n if (signal.aborted) onAbort();\n else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n abortListenerAdded = true;\n }\n }\n\n const onMessage = (ev: { data: unknown }): void => {\n if (finished) return;\n let payload: unknown;\n try {\n const data = typeof ev.data === \"string\" ? ev.data : String(ev.data);\n payload = JSON.parse(data);\n } catch (err) {\n // A malformed frame means this connection is unusable; redial it.\n failure = new CoderAgentError(\"failed to parse chat watch frame\", { cause: err });\n finished = true;\n wake();\n return;\n }\n // chatd sends one event object per frame (wsjson); tolerate batches too.\n if (Array.isArray(payload)) {\n for (const e of payload) queue.push(e as ChatWatchEvent);\n } else if (payload && typeof payload === \"object\") {\n queue.push(payload as ChatWatchEvent);\n }\n wake();\n };\n const onError = (ev: unknown): void => {\n if (finished) return;\n const message =\n ev && typeof ev === \"object\" && \"message\" in ev\n ? String((ev as { message: unknown }).message)\n : \"chat watch socket error\";\n const status = upgradeStatus(message);\n failure =\n status !== undefined && status >= 400 && status < 500\n ? new CoderApiError({ status, method: \"GET\", path: WATCH_PATH, message })\n : new CoderAgentError(message);\n finished = true;\n wake();\n };\n const onClose = (): void => {\n finished = true;\n wake();\n };\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n try {\n while (true) {\n while (queue.length > 0) {\n const next = queue.shift() as ChatWatchEvent;\n // Receiving an event proves the connection works — reset the backoff.\n backoffMs = WATCH_BACKOFF_INITIAL_MS;\n yield next;\n }\n if (finished) break;\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n }\n } finally {\n finished = true;\n if (signal && abortListenerAdded) signal.removeEventListener(\"abort\", onAbort);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n }\n\n if (signal?.aborted) return;\n // A rejected upgrade (4xx) is terminal: retrying with the same credentials\n // against the same server cannot succeed.\n if (failure instanceof CoderApiError) throw failure;\n await sleep(backoffMs, signal);\n if (signal?.aborted) return;\n backoffMs = Math.min(backoffMs * 2, WATCH_BACKOFF_CAP_MS);\n }\n}\n","import { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport { type FileContent, resolveFileContent } from \"../files.js\";\nimport {\n type Chat,\n CHAT_ATTACHMENT_MEDIA_TYPES,\n type ChatMessagesResponse,\n type ChatModelConfig,\n type ChatStreamEvent,\n type ChatWatchEvent,\n type CreateChatMessageRequest,\n type CreateChatMessageResponse,\n type CreateChatRequest,\n MAX_CHAT_FILE_SIZE_BYTES,\n type SubmitToolResultsRequest,\n type UpdateChatRequest,\n type UploadChatFileResponse,\n} from \"./types.js\";\nimport { streamChatEvents, watchChatEvents, type WebSocketFactory } from \"./ws.js\";\n\n/** A file to upload as a chat attachment. */\nexport interface ChatFileInput {\n content: FileContent;\n /**\n * Media type. Required unless `content` is a Blob/File with a non-empty `type`\n * — note `fs.openAsBlob()` returns a Blob with no type, so pass it explicitly there.\n */\n mediaType?: string;\n /** Original filename, surfaced to the model and UI. Defaults to a File's `name`. */\n name?: string;\n}\n\n/** A file that has been uploaded to chat-file storage. */\nexport interface UploadedChatFile {\n /** Server-assigned file id, referenced from message content via a `file` part. */\n id: string;\n mediaType: string;\n name?: string;\n}\n\nexport interface CoderChatClientOptions {\n /** Base URL of the Coder deployment, e.g. `https://dev.coder.com`. */\n baseUrl: string;\n /** Coder API token or session token (sent as `Coder-Session-Token`). */\n token: string;\n /** Custom fetch implementation (defaults to global `fetch`). */\n fetch?: typeof globalThis.fetch;\n /** Custom WebSocket factory (defaults to the `ws` package on Node). */\n webSocketFactory?: WebSocketFactory;\n}\n\nconst API_PREFIX = \"/api/experimental/chats\";\n\n/**\n * A thin, typed client for Coder's experimental `chatd` API. This is a\n * TypeScript port of the chat surface of `codersdk.ExperimentalClient`.\n */\nexport class CoderChatClient {\n readonly baseUrl: string;\n readonly #token: string;\n readonly #fetch: typeof globalThis.fetch;\n readonly #webSocketFactory: WebSocketFactory | undefined;\n\n constructor(options: CoderChatClientOptions) {\n // Normalize: strip a single trailing slash.\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.#token = options.token;\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.#webSocketFactory = options.webSocketFactory;\n }\n\n /**\n * Issue a request and return the raw `Response` on success (body unread). On a\n * non-2xx status the body is consumed to build a {@link CoderApiError}. Shared\n * by JSON requests and the raw upload/download endpoints.\n */\n async #send(\n method: string,\n path: string,\n opts?: { body?: BodyInit; headers?: Record<string, string>; signal?: AbortSignal },\n ): Promise<Response> {\n const headers: Record<string, string> = {\n \"Coder-Session-Token\": this.#token,\n ...opts?.headers,\n };\n const init: RequestInit = { method, headers, body: opts?.body, signal: opts?.signal };\n // A streaming request body requires half-duplex mode on Node's fetch (undici).\n if (typeof ReadableStream !== \"undefined\" && opts?.body instanceof ReadableStream) {\n (init as RequestInit & { duplex?: \"half\" }).duplex = \"half\";\n }\n\n const res = await this.#fetch(`${this.baseUrl}${path}`, init);\n if (!res.ok) {\n const errObj = (await this.#json<{ message?: string; detail?: string }>(res)) ?? {};\n throw new CoderApiError({\n status: res.status,\n method,\n path,\n message: errObj.message ?? res.statusText ?? \"request failed\",\n detail: errObj.detail,\n });\n }\n return res;\n }\n\n /** Read a response body as JSON, tolerating an unreadable/empty/malformed body (→ undefined). */\n async #json<T>(res: Response): Promise<T | undefined> {\n const text = await res.text().catch(() => \"\");\n if (text.length === 0) return undefined;\n try {\n return JSON.parse(text) as T;\n } catch {\n return undefined;\n }\n }\n\n async #request<T>(\n method: string,\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const res = await this.#send(method, path, {\n body: body === undefined ? undefined : JSON.stringify(body),\n headers: body === undefined ? undefined : { \"Content-Type\": \"application/json\" },\n signal,\n });\n return (await this.#json<T>(res)) as T;\n }\n\n // --- REST -----------------------------------------------------------------\n\n listModelConfigs(signal?: AbortSignal): Promise<ChatModelConfig[]> {\n return this.#request<ChatModelConfig[]>(\n \"GET\",\n `${API_PREFIX}/model-configs`,\n undefined,\n signal,\n );\n }\n\n createChat(req: CreateChatRequest, signal?: AbortSignal): Promise<Chat> {\n return this.#request<Chat>(\"POST\", API_PREFIX, req, signal);\n }\n\n getChat(chatId: string, signal?: AbortSignal): Promise<Chat> {\n return this.#request<Chat>(\"GET\", `${API_PREFIX}/${chatId}`, undefined, signal);\n }\n\n createChatMessage(\n chatId: string,\n req: CreateChatMessageRequest,\n signal?: AbortSignal,\n ): Promise<CreateChatMessageResponse> {\n return this.#request<CreateChatMessageResponse>(\n \"POST\",\n `${API_PREFIX}/${chatId}/messages`,\n req,\n signal,\n );\n }\n\n getMessages(\n chatId: string,\n opts?: { before_id?: number; after_id?: number; limit?: number },\n signal?: AbortSignal,\n ): Promise<ChatMessagesResponse> {\n const params = new URLSearchParams();\n if (opts?.before_id !== undefined) params.set(\"before_id\", String(opts.before_id));\n if (opts?.after_id !== undefined) params.set(\"after_id\", String(opts.after_id));\n if (opts?.limit !== undefined) params.set(\"limit\", String(opts.limit));\n const q = params.toString();\n return this.#request<ChatMessagesResponse>(\n \"GET\",\n `${API_PREFIX}/${chatId}/messages${q ? `?${q}` : \"\"}`,\n undefined,\n signal,\n );\n }\n\n submitToolResults(\n chatId: string,\n req: SubmitToolResultsRequest,\n signal?: AbortSignal,\n ): Promise<void> {\n return this.#request<void>(\"POST\", `${API_PREFIX}/${chatId}/tool-results`, req, signal);\n }\n\n /**\n * Interrupt the chat's in-flight run (`POST /interrupt`). The server flips\n * an active run to `interrupting` and responds immediately with the updated\n * chat — it does not wait for the run to actually stop.\n *\n * Pass `{ wait: true }` to send `?wait=true`, asking the server to hold the\n * response until the run has stopped. Coder servers without that param\n * ignore the unknown query harmlessly and still return immediately, so\n * callers should be prepared to confirm completion via the event stream.\n *\n * The second parameter also accepts a bare `AbortSignal` (the historical\n * signature).\n */\n interruptChat(\n chatId: string,\n optsOrSignal?: AbortSignal | { wait?: boolean; signal?: AbortSignal },\n ): Promise<Chat> {\n const opts = optsOrSignal instanceof AbortSignal ? { signal: optsOrSignal } : optsOrSignal;\n const query = opts?.wait ? \"?wait=true\" : \"\";\n return this.#request<Chat>(\n \"POST\",\n `${API_PREFIX}/${chatId}/interrupt${query}`,\n undefined,\n opts?.signal,\n );\n }\n\n updateChat(chatId: string, req: UpdateChatRequest, signal?: AbortSignal): Promise<void> {\n return this.#request<void>(\"PATCH\", `${API_PREFIX}/${chatId}`, req, signal);\n }\n\n /** Convenience: archive a chat (soft-hide; safe for cleanup). */\n archiveChat(chatId: string, signal?: AbortSignal): Promise<void> {\n return this.updateChat(chatId, { archived: true }, signal);\n }\n\n // --- Files ----------------------------------------------------------------\n\n /**\n * Upload a file as a durable chat attachment, returning its id (referenced\n * from message content via a `file` input part). The server enforces a narrow\n * media-type allowlist and a 10 MiB cap; both are checked client-side first so\n * unsupported files fail fast with a clear error instead of an opaque 4xx.\n *\n * The body is sent raw (not multipart): `Content-Type` carries the media type\n * and `Content-Disposition` the filename, mirroring `codersdk`.\n */\n async uploadChatFile(\n organizationId: string,\n file: ChatFileInput,\n signal?: AbortSignal,\n ): Promise<UploadedChatFile> {\n const resolved = resolveFileContent(file.content, {\n mediaType: file.mediaType,\n name: file.name,\n });\n // Match the allowlist on the media type alone, ignoring parameters such as\n // `; charset=utf-8` that a Blob/File commonly carries.\n const mediaType = (resolved.mediaType.split(\";\")[0] ?? resolved.mediaType).trim().toLowerCase();\n if (!CHAT_ATTACHMENT_MEDIA_TYPES.has(mediaType)) {\n throw new CoderAgentError(\n `Media type \"${mediaType}\" is not allowed for chat attachments ` +\n `(allowed: ${[...CHAT_ATTACHMENT_MEDIA_TYPES].join(\", \")}). ` +\n `Write other file types to a workspace instead.`,\n );\n }\n if (resolved.size !== undefined && resolved.size > MAX_CHAT_FILE_SIZE_BYTES) {\n throw new CoderAgentError(\n `File is ${resolved.size} bytes, over the ${MAX_CHAT_FILE_SIZE_BYTES}-byte ` +\n `(10 MiB) chat attachment limit. Write large files to a workspace instead.`,\n );\n }\n\n const headers: Record<string, string> = { \"Content-Type\": mediaType };\n if (resolved.name) {\n // RFC 6266: a sanitized ASCII `filename` plus an RFC 5987 `filename*` that\n // preserves the exact name. Both are pure ASCII, so a non-Latin-1 name\n // (CJK, emoji, accents) can't trip fetch's ByteString header check, and\n // CR/LF/quote/backslash are encoded away rather than breaking the header.\n const ascii = resolved.name.replace(/[^\\x20-\\x7e]/g, \"_\").replace(/([\"\\\\])/g, \"\\\\$1\");\n const utf8 = encodeURIComponent(resolved.name).replace(\n /['()*]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n headers[\"Content-Disposition\"] = `attachment; filename=\"${ascii}\"; filename*=UTF-8''${utf8}`;\n }\n const res = await this.#send(\n \"POST\",\n `${API_PREFIX}/files?organization=${encodeURIComponent(organizationId)}`,\n { body: resolved.body, headers, signal },\n );\n const parsed = await this.#json<UploadChatFileResponse>(res);\n if (!parsed?.id) {\n throw new CoderApiError({\n status: res.status,\n method: \"POST\",\n path: `${API_PREFIX}/files`,\n message: \"upload succeeded but the response contained no file id\",\n });\n }\n return { id: parsed.id, mediaType, name: resolved.name };\n }\n\n /** Download a chat file's bytes and media type by id. */\n async getChatFile(\n fileId: string,\n signal?: AbortSignal,\n ): Promise<{ bytes: Uint8Array; mediaType: string }> {\n const res = await this.#send(\"GET\", `${API_PREFIX}/files/${fileId}`, { signal });\n const buf = await res.arrayBuffer();\n return {\n bytes: new Uint8Array(buf),\n mediaType: res.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n };\n }\n\n // --- Streaming ------------------------------------------------------------\n\n streamEvents(\n chatId: string,\n opts?: { afterId?: number; signal?: AbortSignal },\n ): AsyncGenerator<ChatStreamEvent, void, void> {\n return streamChatEvents({\n baseUrl: this.baseUrl,\n token: this.#token,\n chatId,\n afterId: opts?.afterId,\n signal: opts?.signal,\n webSocketFactory: this.#webSocketFactory,\n });\n }\n\n /**\n * Watch lifecycle events (status/title changes, creation, deletion, …) for\n * every chat visible to the authenticated user via the `/chats/watch`\n * WebSocket. Dropped connections are redialed with exponential backoff; the\n * iteration ends only when `opts.signal` aborts, or with a terminal\n * {@link CoderApiError} when the server rejects the upgrade with a 4xx\n * (bad/expired token, or an older Coder server without the endpoint → 404).\n */\n watchChats(opts?: { signal?: AbortSignal }): AsyncGenerator<ChatWatchEvent, void, void> {\n return watchChatEvents({\n baseUrl: this.baseUrl,\n token: this.#token,\n signal: opts?.signal,\n webSocketFactory: this.#webSocketFactory,\n });\n }\n\n // --- Helpers --------------------------------------------------------------\n\n /**\n * Resolves a user-friendly model hint to a model-config UUID.\n *\n * Accepts: a config UUID (returned as-is), a `provider:model` id\n * (e.g. `anthropic:claude-haiku-4-5-20251001`), a bare model id, or a\n * display-name substring (case-insensitive). Returns `undefined` if no\n * match is found, in which case the caller should let chatd pick the default.\n *\n * Tolerates partial payloads from older/newer servers: a malformed listing\n * (empty body, non-array JSON, null entries) resolves as no match, and\n * entries missing `provider`, `model`, or `display_name` are matched on the\n * fields they do carry — an entry without `provider` still matches by its\n * model id, one without `model` only by display name.\n */\n async resolveModelConfigId(hint: string, signal?: AbortSignal): Promise<string | undefined> {\n const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (UUID_RE.test(hint)) return hint;\n\n const raw: unknown = await this.listModelConfigs(signal);\n const configs = (Array.isArray(raw) ? raw : []).filter(\n (c): c is ChatModelConfig => typeof c === \"object\" && c !== null,\n );\n const lower = hint.toLowerCase();\n // `provider:model` form.\n const colon = hint.indexOf(\":\");\n const provider = colon >= 0 ? hint.slice(0, colon).toLowerCase() : undefined;\n const model = colon >= 0 ? hint.slice(colon + 1).toLowerCase() : lower;\n\n const candidates = configs.filter((c) => c.enabled !== false);\n const pool = candidates.length > 0 ? candidates : configs;\n\n // Guarded accessors: treat a missing/non-string field as absent.\n const modelOf = (c: ChatModelConfig) =>\n typeof c.model === \"string\" ? c.model.toLowerCase() : undefined;\n const providerOf = (c: ChatModelConfig) =>\n typeof c.provider === \"string\" ? c.provider.toLowerCase() : undefined;\n const displayNameOf = (c: ChatModelConfig) =>\n typeof c.display_name === \"string\" ? c.display_name.toLowerCase() : undefined;\n\n const exact = pool.find(\n (c) => modelOf(c) === model && (provider === undefined || providerOf(c) === provider),\n );\n if (exact) return exact.id;\n\n const byModel = pool.find((c) => modelOf(c) === model);\n if (byModel) return byModel.id;\n\n const byDisplay = pool.find((c) => displayNameOf(c)?.includes(lower));\n if (byDisplay) return byDisplay.id;\n\n const byModelSubstring = pool.find((c) => modelOf(c)?.includes(model));\n return byModelSubstring?.id;\n }\n}\n","/**\n * Minimal helpers for the stable Coder v2 REST endpoints that the workspace\n * preview feature composes: workspace lookup, the wildcard app host, and port\n * sharing. Deliberately tiny — only the fields this package reads are modeled\n * — and mirroring `CoderChatClient`'s conventions (`Coder-Session-Token`\n * auth, `CoderApiError` on non-2xx). These are internal building blocks for\n * `CoderAgent.getPreview`/`sharePreview`; only their option/result types are\n * part of the public API.\n */\n\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\n\n/** Connection details for the stable v2 API (same credentials as the chat client). */\nexport interface WorkspaceApiConnection {\n /** Base URL of the Coder deployment, e.g. `https://dev.coder.com`. */\n baseUrl: string;\n /** Coder API/session token (sent as `Coder-Session-Token`). */\n token: string;\n /** Custom fetch implementation (defaults to global `fetch`). */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * Subset of `codersdk.Workspace` (GET `/api/v2/workspaces/{workspace}`) that\n * the preview helpers read. Resources without agents omit the `agents` key\n * entirely, so the whole chain is treated as optional.\n */\nexport interface WorkspaceSummary {\n id: string;\n /** Username of the workspace owner (a subdomain URL segment). */\n owner_name: string;\n name: string;\n latest_build?: {\n resources?: { agents?: { name: string }[] }[];\n };\n}\n\n/** Response of GET `/api/v2/applications/host`; `host` is `\"\"` when subdomain apps are disabled. */\nexport interface AppHostResponse {\n /** Wildcard app host with a literal `*`, e.g. `*.apps.example.com`, possibly with a port. */\n host: string;\n}\n\n/** Share levels a port can be in; `owner` is the implicit default when no share exists. */\nexport type PortShareLevel = \"owner\" | \"authenticated\" | \"organization\" | \"public\";\n\n/**\n * Share levels the upsert endpoint accepts. `owner` is rejected server-side —\n * reverting to owner-only is done by deleting the share, not setting it.\n */\nexport type PreviewShareLevel = Exclude<PortShareLevel, \"owner\">;\n\n/** Protocol the workspace app speaks on the port (drives the `s` suffix in the URL). */\nexport type PortShareProtocol = \"http\" | \"https\";\n\n/** Body of POST `/api/v2/workspaces/{workspace}/port-share` (a true upsert on agent+port). */\nexport interface UpsertPortShareRequest {\n agent_name: string;\n port: number;\n share_level: PreviewShareLevel;\n protocol: PortShareProtocol;\n}\n\n/** A port-share row, mirroring `codersdk.WorkspaceAgentPortShare`. */\nexport interface WorkspaceAgentPortShare {\n workspace_id: string;\n agent_name: string;\n port: number;\n share_level: PortShareLevel;\n protocol: PortShareProtocol;\n}\n\n/**\n * Issue a JSON request against the v2 API. Mirrors `CoderChatClient`: the\n * session-token header, and a {@link CoderApiError} carrying the server's\n * `message`/`detail` on any non-2xx (body tolerated missing or malformed).\n */\nasync function request<T>(\n conn: WorkspaceApiConnection,\n method: string,\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n): Promise<T> {\n const fetchFn = conn.fetch ?? globalThis.fetch.bind(globalThis);\n const headers: Record<string, string> = { \"Coder-Session-Token\": conn.token };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n\n const res = await fetchFn(`${conn.baseUrl.replace(/\\/$/, \"\")}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n const text = await res.text().catch(() => \"\");\n let parsed: unknown;\n try {\n parsed = text.length > 0 ? JSON.parse(text) : undefined;\n } catch {\n parsed = undefined;\n }\n if (!res.ok) {\n const errObj = (parsed ?? {}) as { message?: string; detail?: string };\n throw new CoderApiError({\n status: res.status,\n method,\n path,\n message: errObj.message ?? res.statusText ?? \"request failed\",\n detail: errObj.detail,\n });\n }\n return parsed as T;\n}\n\n/** Fetch a workspace (owner, name, agents) by UUID: GET `/api/v2/workspaces/{workspace}`. */\nexport function getWorkspace(\n conn: WorkspaceApiConnection,\n workspaceId: string,\n signal?: AbortSignal,\n): Promise<WorkspaceSummary> {\n return request<WorkspaceSummary>(\n conn,\n \"GET\",\n `/api/v2/workspaces/${workspaceId}`,\n undefined,\n signal,\n );\n}\n\n/**\n * Fetch the deployment's wildcard app host: GET `/api/v2/applications/host`.\n * The endpoint predates port sharing and exists on all supported servers;\n * an empty `host` means no wildcard access URL is configured.\n */\nexport function getAppsHost(\n conn: WorkspaceApiConnection,\n signal?: AbortSignal,\n): Promise<AppHostResponse> {\n return request<AppHostResponse>(conn, \"GET\", \"/api/v2/applications/host\", undefined, signal);\n}\n\n/**\n * Create or update a port share: POST `/api/v2/workspaces/{workspace}/port-share`\n * (an upsert keyed on agent+port; re-posting updates level/protocol in place).\n * A 404 is rethrown with a hint that the server may predate port sharing —\n * the whole route is absent on Coder servers older than ~v2.9.\n */\nexport async function upsertPortShare(\n conn: WorkspaceApiConnection,\n workspaceId: string,\n req: UpsertPortShareRequest,\n signal?: AbortSignal,\n): Promise<WorkspaceAgentPortShare> {\n const path = `/api/v2/workspaces/${workspaceId}/port-share`;\n try {\n return await request<WorkspaceAgentPortShare>(conn, \"POST\", path, req, signal);\n } catch (err) {\n if (err instanceof CoderApiError && err.status === 404) {\n throw new CoderApiError({\n status: 404,\n method: \"POST\",\n path,\n message: \"port sharing is unavailable\",\n detail:\n \"the workspace was not found, or this Coder server predates port sharing (added in Coder v2.9)\",\n });\n }\n throw err;\n }\n}\n\n/** Inputs for building a preview URL (see `CoderAgent.getPreview`). */\nexport interface ResolvePreviewOptions {\n workspaceId: string;\n /** Workspace port the app listens on. */\n port: number;\n /** Agent that serves the port; defaults to the workspace's only agent. */\n agentName?: string;\n /** Protocol the app speaks inside the workspace (not the browser scheme). Default `\"http\"`. */\n protocol?: PortShareProtocol;\n}\n\n/** The workspace's only agent's name, or a clear error naming the candidates. */\nfunction defaultAgentName(workspace: WorkspaceSummary): string {\n const agents = (workspace.latest_build?.resources ?? []).flatMap((r) => r.agents ?? []);\n const first = agents[0];\n if (agents.length === 1 && first) return first.name;\n if (agents.length === 0) {\n throw new CoderAgentError(\n `Workspace \"${workspace.name}\" has no agents in its latest build (is it running?); ` +\n `pass agentName explicitly.`,\n );\n }\n throw new CoderAgentError(\n `Workspace \"${workspace.name}\" has ${agents.length} agents ` +\n `(${agents.map((a) => a.name).join(\", \")}); pass agentName to pick one.`,\n );\n}\n\n/**\n * Ports must be integral, in TCP range, and — stricter than TCP — at least\n * 1000: Coder's subdomain app parser (`appurl.PortRegex`, `^\\d{4,5}s?$`) only\n * treats 4–5 digit labels as ports, so a URL for port 80 would be parsed as an\n * app named \"80\" and never resolve. Reject those up front instead of returning\n * a dead URL.\n */\nfunction assertValidPort(port: number): void {\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new CoderAgentError(`Invalid port ${port}: expected an integer between 1 and 65535.`);\n }\n if (port < 1000) {\n throw new CoderAgentError(\n `Port ${port} cannot be previewed: Coder subdomain app URLs only encode 4-5 digit ports ` +\n `(1000-65535). Serve the preview on a higher port instead.`,\n );\n }\n}\n\n/**\n * Compose the subdomain preview URL for a workspace port the same way the\n * dashboard does: the label `{port}{s?}--{agent}--{workspace}--{owner}`\n * replaces the `*` in the deployment's wildcard app host, and the browser\n * scheme follows the deployment's own access URL. The trailing `s` (from\n * `protocol: \"https\"`) tells the proxy to speak TLS *to the workspace port*;\n * it does not affect the browser scheme.\n */\nexport async function resolveWorkspacePreview(\n conn: WorkspaceApiConnection,\n opts: ResolvePreviewOptions,\n signal?: AbortSignal,\n): Promise<{ url: string; agentName: string }> {\n assertValidPort(opts.port);\n const [workspace, appHost] = await Promise.all([\n getWorkspace(conn, opts.workspaceId, signal),\n getAppsHost(conn, signal),\n ]);\n if (!appHost.host) {\n throw new CoderAgentError(\n \"This Coder deployment has no wildcard app host configured (--wildcard-access-url), \" +\n \"so subdomain preview URLs are unavailable.\",\n );\n }\n const agentName = opts.agentName ?? defaultAgentName(workspace);\n const suffix = opts.protocol === \"https\" ? \"s\" : \"\";\n const label = `${opts.port}${suffix}--${agentName}--${workspace.name}--${workspace.owner_name}`;\n // The host keeps a literal `*` (possibly inside the first label, e.g.\n // `*--apps.example.com`) and may carry an explicit port on dev deployments —\n // substitute the label into the whole host string, dashboard-style.\n const scheme = new URL(conn.baseUrl).protocol;\n return { url: `${scheme}//${appHost.host.replace(/\\*/g, label)}`, agentName };\n}\n\n/** Inputs for sharing and previewing a workspace port (see `CoderAgent.sharePreview`). */\nexport interface SharePreviewRequest extends ResolvePreviewOptions {\n /** Who may open the port. Default `\"authenticated\"`. */\n shareLevel?: PreviewShareLevel;\n}\n\n/**\n * Resolve a port's preview URL, then upsert its share level so the URL is\n * reachable beyond the workspace owner. Returns the server-confirmed level\n * (falling back to the requested one when the server omits it).\n */\nexport async function shareWorkspacePreview(\n conn: WorkspaceApiConnection,\n opts: SharePreviewRequest,\n signal?: AbortSignal,\n): Promise<{ url: string; shareLevel: PreviewShareLevel }> {\n const requested = opts.shareLevel ?? \"authenticated\";\n const { url, agentName } = await resolveWorkspacePreview(conn, opts, signal);\n const share = await upsertPortShare(\n conn,\n opts.workspaceId,\n {\n agent_name: agentName,\n port: opts.port,\n share_level: requested,\n protocol: opts.protocol ?? \"http\",\n },\n signal,\n );\n // Old servers may answer with an empty/partial body; trust it when present.\n const level = share?.share_level && share.share_level !== \"owner\" ? share.share_level : requested;\n return { url, shareLevel: level };\n}\n","import type {\n LanguageModelV4CallOptions,\n LanguageModelV4Message,\n LanguageModelV4Prompt,\n LanguageModelV4ToolResultOutput,\n SharedV4FileData,\n} from \"@ai-sdk/provider\";\nimport type { ChatInputPart, DynamicTool, ToolResult } from \"../coder/types.js\";\n\n/** Namespace key for this package's options on AI SDK message-part `providerOptions`. */\nexport const CODER_PROVIDER_OPTIONS = \"coder\";\n\n/** Shape of this package's `providerOptions.coder` payload on a file part. */\nexport interface CoderFileProviderOptions {\n /** A pre-uploaded chat-file id to reuse instead of uploading the part's bytes. */\n fileId?: string;\n}\n\n/** Concatenated system messages, mapped to chatd's `system_prompt`. */\nexport function extractSystemPrompt(prompt: LanguageModelV4Prompt): string | undefined {\n const parts = prompt.filter(\n (m): m is Extract<LanguageModelV4Message, { role: \"system\" }> => m.role === \"system\",\n );\n if (parts.length === 0) return undefined;\n const joined = parts\n .map((m) => m.content)\n .join(\"\\n\\n\")\n .trim();\n return joined.length > 0 ? joined : undefined;\n}\n\n/** The content array of a user message (text and file parts). */\nexport type UserContent = Extract<LanguageModelV4Message, { role: \"user\" }>[\"content\"];\n\n/**\n * Uploads a file part's bytes and resolves to its chatd file id. Supplied by\n * the language model (which holds the client + organization id); kept abstract\n * here so {@link userContentToInputParts} stays pure and testable.\n */\nexport type FilePartUploader = (file: {\n data: SharedV4FileData;\n mediaType: string;\n filename?: string;\n}) => Promise<string>;\n\n/** Reads a pre-uploaded file id from a file part's `providerOptions.coder.fileId`. */\nfunction coderFileId(providerOptions: unknown): string | undefined {\n const ns = (providerOptions as Record<string, CoderFileProviderOptions> | undefined)?.[\n CODER_PROVIDER_OPTIONS\n ];\n // Require a non-empty id: an empty string is not a usable reference, so fall\n // back to uploading rather than emitting `file_id: \"\"`.\n return typeof ns?.fileId === \"string\" && ns.fileId.length > 0 ? ns.fileId : undefined;\n}\n\n/**\n * Maps a user message's content to chatd input parts. Text passes through;\n * file parts are turned into `file` parts referencing an uploaded file id —\n * either reused from `providerOptions.coder.fileId` (a pre-uploaded handle) or\n * uploaded on the fly via {@link FilePartUploader}.\n */\nexport async function userContentToInputParts(\n content: UserContent,\n uploadFile: FilePartUploader,\n): Promise<ChatInputPart[]> {\n // Uploads are independent, so run them concurrently; `map` preserves order.\n const parts = await Promise.all(\n content.map(async (part): Promise<ChatInputPart | null> => {\n if (part.type === \"text\") {\n return part.text.length > 0 ? { type: \"text\", text: part.text } : null;\n }\n if (part.type === \"file\") {\n const fileId =\n coderFileId(part.providerOptions) ??\n (await uploadFile({\n data: part.data,\n mediaType: part.mediaType,\n filename: part.filename,\n }));\n return { type: \"file\", file_id: fileId };\n }\n return null;\n }),\n );\n return parts.filter((p): p is ChatInputPart => p !== null);\n}\n\nfunction toolResultOutputToChatd(output: LanguageModelV4ToolResultOutput): {\n value: unknown;\n isError: boolean;\n} {\n switch (output.type) {\n case \"text\":\n return { value: output.value, isError: false };\n case \"json\":\n return { value: output.value, isError: false };\n case \"error-text\":\n return { value: output.value, isError: true };\n case \"error-json\":\n return { value: output.value, isError: true };\n case \"execution-denied\":\n return { value: { execution_denied: true, reason: output.reason }, isError: true };\n case \"content\":\n return { value: output.value, isError: false };\n default:\n return { value: output, isError: false };\n }\n}\n\nexport type TurnAction =\n | { kind: \"new-turn\"; content: UserContent }\n | { kind: \"resume\"; toolResults: ToolResult[] }\n | { kind: \"noop\" };\n\n/**\n * Decides what to do with a fresh `doStream`/`doGenerate` prompt:\n * - trailing `tool` message(s) → resume the in-flight chatd turn by submitting\n * those tool results (the AI SDK executed a client tool between steps);\n * - otherwise the trailing `user` message → a new turn.\n */\nexport function classifyTurnAction(prompt: LanguageModelV4Prompt): TurnAction {\n if (prompt.length === 0) return { kind: \"noop\" };\n const last = prompt[prompt.length - 1];\n if (!last) return { kind: \"noop\" };\n\n if (last.role === \"tool\") {\n const toolResults: ToolResult[] = [];\n // Collect from all trailing consecutive tool messages.\n for (let i = prompt.length - 1; i >= 0; i--) {\n const m = prompt[i];\n if (!m || m.role !== \"tool\") break;\n for (const part of m.content) {\n if (part.type !== \"tool-result\") continue;\n const { value, isError } = toolResultOutputToChatd(part.output);\n toolResults.unshift({ tool_call_id: part.toolCallId, output: value, is_error: isError });\n }\n }\n return { kind: \"resume\", toolResults };\n }\n\n if (last.role === \"user\") {\n return { kind: \"new-turn\", content: last.content };\n }\n\n return { kind: \"noop\" };\n}\n\n/** Maps AI SDK function tools to chatd client-executed (\"dynamic\") tools. */\nexport function toolsToDynamicTools(tools: LanguageModelV4CallOptions[\"tools\"]): DynamicTool[] {\n if (!tools) return [];\n const out: DynamicTool[] = [];\n for (const tool of tools) {\n if (tool.type !== \"function\") continue; // provider tools are not forwarded\n out.push({\n name: tool.name,\n description: tool.description,\n input_schema: tool.inputSchema,\n });\n }\n return out;\n}\n\n/** The set of tool names the AI SDK side owns (i.e. client-executed). */\nexport function dynamicToolNames(tools: LanguageModelV4CallOptions[\"tools\"]): Set<string> {\n const names = new Set<string>();\n for (const tool of tools ?? []) {\n if (tool.type === \"function\") names.add(tool.name);\n }\n return names;\n}\n","import type {\n JSONObject,\n LanguageModelV4FinishReason,\n LanguageModelV4StreamPart,\n LanguageModelV4Usage,\n} from \"@ai-sdk/provider\";\nimport { CoderChatError } from \"../errors.js\";\nimport {\n type ChatErrorPayload,\n type ChatMessage,\n type ChatMessagePart,\n type ChatMessageUsage,\n type ChatStatus,\n type ChatStreamEvent,\n TERMINAL_STATUSES,\n} from \"../coder/types.js\";\n\nfunction mapUsage(u: ChatMessageUsage | undefined): LanguageModelV4Usage {\n return {\n inputTokens: {\n total: u?.input_tokens,\n noCache: undefined,\n cacheRead: u?.cache_read_tokens,\n cacheWrite: u?.cache_creation_tokens,\n },\n outputTokens: {\n total: u?.output_tokens,\n text: undefined,\n reasoning: u?.reasoning_tokens,\n },\n // Preserve the verbatim wire usage (snake_case) so callers can reach fields\n // the normalized shape has no slot for (context_limit, cost, runtime, …).\n ...(u ? { raw: u as unknown as JSONObject } : {}),\n };\n}\n\nfunction jsonResult(value: unknown): NonNullable<unknown> {\n // chatd tool results are arbitrary JSON; the V4 tool-result `result` must be\n // a non-null JSON value.\n return (value ?? {}) as NonNullable<unknown>;\n}\n\n/**\n * Translates one chatd turn's {@link ChatStreamEvent} stream into a sequence of\n * `LanguageModelV4StreamPart`s for the AI SDK.\n *\n * Two text/reasoning modes, decided per assistant message from the wire\n * behavior:\n * - **delta mode** — chatd streamed `message_part` deltas; we emit those and\n * treat the message's trailing full `message` snapshot as a no-op for\n * text/reasoning. Deltas carry no message id, so \"deltas arrived since the\n * last assistant snapshot\" is what marks a snapshot as trailing.\n * - **snapshot mode** — fast turns where only full `message` snapshots arrive;\n * we diff each snapshot's full text against what we've already emitted.\n * Both paths track an emitted-length cursor, so neither double-counts.\n *\n * Client (custom) tool calls are emitted from the reliable `action_required`\n * event and left for the AI SDK to execute. chatd's own server-side tools are\n * surfaced best-effort as `providerExecuted` tool calls/results.\n */\nexport class TurnTranslator {\n readonly #dynamicToolNames: ReadonlySet<string>;\n\n #seq = 0;\n #text = { id: undefined as string | undefined, len: 0, sawDelta: false };\n #reasoning = { id: undefined as string | undefined, len: 0, sawDelta: false };\n #currentAssistantId: number | undefined;\n // Whether text/reasoning deltas arrived since the last assistant `message`\n // snapshot — i.e. the next assistant snapshot is the trailing snapshot of the\n // message those deltas streamed (deltas carry no message id of their own).\n #deltasSinceSnapshot = false;\n\n #serverToolCalls = new Set<string>();\n #serverToolResults = new Set<string>();\n #clientToolCalls = new Set<string>();\n #clientToolCallSeen = false;\n #sources = new Set<string>();\n\n #usage: ChatMessageUsage | undefined;\n #error: ChatErrorPayload | undefined;\n #terminalStatus: ChatStatus | undefined;\n #maxMessageId = 0;\n\n constructor(opts: { dynamicToolNames: ReadonlySet<string> }) {\n this.#dynamicToolNames = opts.dynamicToolNames;\n }\n\n get terminalStatus(): ChatStatus | undefined {\n return this.#terminalStatus;\n }\n /** Whether a client (custom) tool call has been emitted this turn. */\n get clientToolCallSeen(): boolean {\n return this.#clientToolCallSeen;\n }\n get maxMessageId(): number {\n return this.#maxMessageId;\n }\n get error(): ChatErrorPayload | undefined {\n return this.#error;\n }\n\n // --- block helpers --------------------------------------------------------\n\n #openText(out: LanguageModelV4StreamPart[]): void {\n if (this.#reasoning.id) this.#closeReasoning(out);\n if (!this.#text.id) {\n this.#text.id = `text-${++this.#seq}`;\n this.#text.len = 0;\n out.push({ type: \"text-start\", id: this.#text.id });\n }\n }\n #closeText(out: LanguageModelV4StreamPart[]): void {\n if (this.#text.id) {\n out.push({ type: \"text-end\", id: this.#text.id });\n this.#text.id = undefined;\n this.#text.len = 0;\n }\n }\n #openReasoning(out: LanguageModelV4StreamPart[]): void {\n if (this.#text.id) this.#closeText(out);\n if (!this.#reasoning.id) {\n this.#reasoning.id = `reasoning-${++this.#seq}`;\n this.#reasoning.len = 0;\n out.push({ type: \"reasoning-start\", id: this.#reasoning.id });\n }\n }\n #closeReasoning(out: LanguageModelV4StreamPart[]): void {\n if (this.#reasoning.id) {\n out.push({ type: \"reasoning-end\", id: this.#reasoning.id });\n this.#reasoning.id = undefined;\n this.#reasoning.len = 0;\n }\n }\n\n #emitTextUpTo(out: LanguageModelV4StreamPart[], full: string): void {\n if (full.length <= this.#text.len && this.#text.id) return;\n this.#openText(out);\n if (full.length > this.#text.len) {\n out.push({\n type: \"text-delta\",\n id: this.#text.id as string,\n delta: full.slice(this.#text.len),\n });\n this.#text.len = full.length;\n }\n }\n #emitReasoningUpTo(out: LanguageModelV4StreamPart[], full: string): void {\n if (full.length <= this.#reasoning.len && this.#reasoning.id) return;\n this.#openReasoning(out);\n if (full.length > this.#reasoning.len) {\n out.push({\n type: \"reasoning-delta\",\n id: this.#reasoning.id as string,\n delta: full.slice(this.#reasoning.len),\n });\n this.#reasoning.len = full.length;\n }\n }\n\n // --- tool helpers ---------------------------------------------------------\n\n #isClientTool(name: string | undefined): boolean {\n return name !== undefined && this.#dynamicToolNames.has(name);\n }\n\n #emitServerToolCall(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const id = part.tool_call_id;\n const name = part.tool_name;\n if (!id || !name) return;\n if (part.args === undefined) return; // wait for complete args (snapshot)\n if (this.#serverToolCalls.has(id)) return;\n this.#serverToolCalls.add(id);\n // `dynamic: true` is load-bearing: server tools are not in the client ToolSet, and\n // the AI SDK only tolerates unknown tool names on `providerExecuted && dynamic`\n // calls. Without it every server tool call is marked `invalid`, which injects a\n // phantom tool-error output and halts the tool loop on that step — stranding the\n // turn whenever a client tool call is pending in the same segment.\n out.push({\n type: \"tool-input-start\",\n id,\n toolName: name,\n providerExecuted: true,\n dynamic: true,\n });\n out.push({ type: \"tool-input-end\", id });\n out.push({\n type: \"tool-call\",\n toolCallId: id,\n toolName: name,\n input: typeof part.args === \"string\" ? part.args : JSON.stringify(part.args),\n providerExecuted: true,\n dynamic: true,\n });\n }\n\n #emitServerToolResult(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const id = part.tool_call_id;\n const name = part.tool_name;\n if (!id || !name) return;\n if (part.result === undefined) return;\n if (this.#serverToolResults.has(id)) return;\n // Only pair with a call emitted in THIS segment. A result whose call streamed in\n // an earlier segment (chatd paused for a client tool in between, and the resume\n // segment starts past the assistant message) would reach the AI SDK call-less,\n // and generateText throws \"Tool call <id> not found.\" Drop the orphan — the\n // server-side transcript still has it.\n if (!this.#serverToolCalls.has(id)) return;\n this.#serverToolResults.add(id);\n out.push({\n type: \"tool-result\",\n toolCallId: id,\n toolName: name,\n result: jsonResult(part.result),\n isError: part.is_error ?? false,\n // Mirror the call's `dynamic: true` so call and result land in the same\n // bucket (steps[*].dynamicToolCalls / dynamicToolResults, UI streams).\n dynamic: true,\n });\n }\n\n /**\n * Emits a chatd `source` part as a standalone V4 url source (no text-block\n * bracketing needed). Deduped by the emitted id so a part streamed as a\n * `message_part` isn't re-emitted by its trailing `message` snapshot, while\n * snapshot-only turns still emit theirs.\n */\n #emitSource(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const url = part.url;\n if (!url) return; // the V4 source part requires both id and url\n const id = part.source_id || url;\n if (this.#sources.has(id)) return;\n this.#sources.add(id);\n out.push({\n type: \"source\",\n sourceType: \"url\",\n id,\n url,\n ...(part.title !== undefined ? { title: part.title } : {}),\n });\n }\n\n // --- ingest ---------------------------------------------------------------\n\n ingest(ev: ChatStreamEvent): LanguageModelV4StreamPart[] {\n const out: LanguageModelV4StreamPart[] = [];\n switch (ev.type) {\n case \"message_part\":\n this.#ingestMessagePart(out, ev);\n break;\n case \"message\":\n if (ev.message) this.#ingestMessage(out, ev.message);\n break;\n case \"action_required\":\n for (const tc of ev.action_required?.tool_calls ?? []) {\n if (this.#clientToolCalls.has(tc.tool_call_id)) continue;\n this.#closeText(out);\n this.#closeReasoning(out);\n this.#clientToolCalls.add(tc.tool_call_id);\n this.#clientToolCallSeen = true;\n out.push({ type: \"tool-input-start\", id: tc.tool_call_id, toolName: tc.tool_name });\n out.push({ type: \"tool-input-end\", id: tc.tool_call_id });\n out.push({\n type: \"tool-call\",\n toolCallId: tc.tool_call_id,\n toolName: tc.tool_name,\n input: tc.args,\n });\n }\n break;\n case \"error\":\n if (ev.error) {\n this.#error = ev.error;\n out.push({ type: \"error\", error: new CoderChatError(ev.error) });\n }\n break;\n case \"status\":\n if (ev.status && TERMINAL_STATUSES.has(ev.status.status)) {\n this.#terminalStatus = ev.status.status;\n }\n break;\n default:\n break; // retry / queue_update / preview_reset / history_reset\n }\n return out;\n }\n\n #ingestMessagePart(out: LanguageModelV4StreamPart[], ev: ChatStreamEvent): void {\n const mp = ev.message_part;\n if (!mp || (mp.role !== \"assistant\" && mp.role !== \"tool\")) return;\n const part = mp.part;\n switch (part.type) {\n case \"text\":\n this.#text.sawDelta = true;\n this.#deltasSinceSnapshot = true;\n this.#openText(out);\n if (part.text) {\n out.push({ type: \"text-delta\", id: this.#text.id as string, delta: part.text });\n this.#text.len += part.text.length;\n }\n break;\n case \"reasoning\":\n this.#reasoning.sawDelta = true;\n this.#deltasSinceSnapshot = true;\n this.#openReasoning(out);\n if (part.text) {\n out.push({ type: \"reasoning-delta\", id: this.#reasoning.id as string, delta: part.text });\n this.#reasoning.len += part.text.length;\n }\n break;\n case \"tool-call\":\n this.#closeText(out);\n this.#closeReasoning(out);\n if (!this.#isClientTool(part.tool_name)) this.#emitServerToolCall(out, part);\n break;\n case \"tool-result\":\n if (!this.#isClientTool(part.tool_name)) this.#emitServerToolResult(out, part);\n break;\n case \"source\":\n this.#emitSource(out, part);\n break;\n default:\n break;\n }\n }\n\n #ingestMessage(out: LanguageModelV4StreamPart[], message: ChatMessage): void {\n if (message.id > this.#maxMessageId) this.#maxMessageId = message.id;\n if (message.usage && (message.role === \"assistant\" || message.role === \"tool\")) {\n this.#usage = message.usage;\n }\n const content = message.content ?? [];\n\n if (message.role === \"assistant\") {\n // New assistant message boundary: close prior blocks and reset cursors.\n // Skipped when deltas arrived since the previous assistant snapshot:\n // deltas carry no message id, so they belong to the message THIS snapshot\n // finalizes (its id differing from the previous snapshot's), and the\n // snapshot must stay a no-op for text/reasoning. The reset is load-bearing\n // for snapshot-only messages, which must still diff-and-emit below. Known\n // tradeoff: a mid-message snapshot followed by more deltas of the SAME\n // message would misclassify the next snapshot — that ordering is outside\n // the trailing-snapshot protocol (see class doc).\n if (\n this.#currentAssistantId !== undefined &&\n this.#currentAssistantId !== message.id &&\n !this.#deltasSinceSnapshot\n ) {\n this.#closeText(out);\n this.#closeReasoning(out);\n this.#text.sawDelta = false;\n this.#reasoning.sawDelta = false;\n }\n this.#currentAssistantId = message.id;\n\n if (!this.#text.sawDelta) {\n const full = content\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n if (full.length > 0) this.#emitTextUpTo(out, full);\n }\n if (!this.#reasoning.sawDelta) {\n const full = content\n .filter((p) => p.type === \"reasoning\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n if (full.length > 0) this.#emitReasoningUpTo(out, full);\n }\n // Tool calls/results and sources are id-deduped, so snapshots process them\n // unconditionally (even when the snapshot is a text/reasoning no-op).\n for (const part of content) {\n if (part.type === \"tool-call\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolCall(out, part);\n else if (part.type === \"tool-result\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolResult(out, part);\n else if (part.type === \"source\") this.#emitSource(out, part);\n }\n this.#deltasSinceSnapshot = false;\n } else if (message.role === \"tool\") {\n for (const part of content) {\n if (part.type === \"tool-result\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolResult(out, part);\n else if (part.type === \"source\") this.#emitSource(out, part);\n }\n }\n }\n\n // --- finish ---------------------------------------------------------------\n\n finish(): LanguageModelV4StreamPart[] {\n const out: LanguageModelV4StreamPart[] = [];\n this.#closeText(out);\n this.#closeReasoning(out);\n\n let unified: LanguageModelV4FinishReason[\"unified\"];\n if (this.#error || this.#terminalStatus === \"error\") unified = \"error\";\n else if (this.#clientToolCallSeen || this.#terminalStatus === \"requires_action\")\n unified = \"tool-calls\";\n else unified = \"stop\";\n\n // Surface server-side cost/runtime (sent by newer servers as extra usage\n // fields) verbatim under `providerMetadata.coder`. Omitted entirely when\n // the server sent neither, so old servers look unchanged to callers.\n const coder: JSONObject = {};\n if (this.#usage?.total_cost_micros !== undefined)\n coder.total_cost_micros = this.#usage.total_cost_micros;\n if (this.#usage?.total_runtime_ms !== undefined)\n coder.total_runtime_ms = this.#usage.total_runtime_ms;\n\n out.push({\n type: \"finish\",\n usage: mapUsage(this.#usage),\n finishReason: { unified, raw: this.#terminalStatus },\n ...(Object.keys(coder).length > 0 ? { providerMetadata: { coder } } : {}),\n });\n return out;\n }\n}\n","import type {\n LanguageModelV4,\n LanguageModelV4CallOptions,\n LanguageModelV4Content,\n LanguageModelV4GenerateResult,\n LanguageModelV4StreamPart,\n LanguageModelV4StreamResult,\n LanguageModelV4Usage,\n SharedV4Warning,\n} from \"@ai-sdk/provider\";\nimport { assertSupportedAiVersion } from \"../ai-version.js\";\nimport { CoderAgentError, CoderApiError, CoderChatError } from \"../errors.js\";\nimport { CoderChatClient } from \"../coder/client.js\";\nimport type { ChatInputPart, CreateChatRequest } from \"../coder/types.js\";\nimport { dataContentToFileContent } from \"../files.js\";\nimport {\n classifyTurnAction,\n dynamicToolNames,\n extractSystemPrompt,\n type FilePartUploader,\n toolsToDynamicTools,\n type UserContent,\n userContentToInputParts,\n} from \"./prompt.js\";\nimport { TurnTranslator } from \"./translate.js\";\n\nconst EMPTY_USAGE: LanguageModelV4Usage = {\n inputTokens: {\n total: undefined,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: { total: undefined, text: undefined, reasoning: undefined },\n};\n\nexport interface CoderLanguageModelConfig {\n client: CoderChatClient;\n organizationId: string;\n /** Model hint (UUID, `provider:model`, model id, or display-name substring). */\n model?: string;\n /** Bind the chat to a Coder workspace (enables workspace tools). */\n workspaceId?: string;\n /** chatd-side MCP servers to enable. */\n mcpServerIds?: string[];\n planMode?: \"\" | \"plan\";\n /** Resume an existing chat instead of creating a new one. */\n chatId?: string;\n /**\n * Per-segment time budget in milliseconds, applied to each model round-trip\n * (one `doStream`/`doGenerate` call: chat creation or message/tool-result\n * submission, plus the server-side run until it settles or pauses for a client\n * tool). If exceeded, the run is interrupted server-side and the call rejects\n * with a retryable {@link CoderChatError} (`kind: \"timeout\"`). A multi-step\n * `generate()` that drives client tools makes several segments, so this bounds\n * each segment, not the whole call — to cap total wall-clock, pass\n * `abortSignal: AbortSignal.timeout(ms)`. Unset or non-positive means no limit.\n */\n requestTimeoutMs?: number;\n}\n\n/**\n * A {@link LanguageModelV4} that is backed by a remote Coder `chatd` agent\n * runtime instead of a raw LLM. One model instance owns one chatd chat\n * (a \"session\"): the chat is created lazily on the first turn and reused for\n * subsequent turns and for client-tool resume steps.\n *\n * The chatd server runs the agent loop (model calls, server-side tools,\n * compaction) itself. This model therefore represents *one chatd segment* per\n * `doStream` call — it advances the chat until it settles (`waiting`/\n * `completed`) or pauses for a client tool (`requires_action`). When the AI SDK\n * executes a client tool and calls `doStream` again with the tool result, this\n * model resumes the same chat. The two loops mesh at the client-tool boundary.\n *\n * NOTE: a single model instance is single-flight — do not run concurrent\n * generations against the same instance/session.\n */\nexport class CoderLanguageModel implements LanguageModelV4 {\n readonly specificationVersion = \"v4\" as const;\n readonly provider = \"coder.chatd\";\n readonly modelId: string;\n readonly supportedUrls: Record<string, RegExp[]> = {};\n\n readonly #config: CoderLanguageModelConfig;\n #chatId: string | undefined;\n #lastSeenMessageId = 0;\n #resolvedModelConfigId: string | undefined;\n #modelResolved = false;\n readonly #submittedToolCallIds = new Set<string>();\n #inFlight = false;\n\n constructor(config: CoderLanguageModelConfig) {\n // Fail fast on an incompatible AI SDK major (see peer dependency `ai@^7`).\n assertSupportedAiVersion();\n this.#config = config;\n this.modelId = config.model ?? \"chatd\";\n this.#chatId = config.chatId;\n }\n\n get chatId(): string | undefined {\n return this.#chatId;\n }\n\n /** Drops the current session so the next turn creates a fresh chat. */\n resetSession(): void {\n this.#chatId = undefined;\n this.#lastSeenMessageId = 0;\n this.#submittedToolCallIds.clear();\n }\n\n async #resolveModelConfigId(signal?: AbortSignal): Promise<string | undefined> {\n if (this.#modelResolved) return this.#resolvedModelConfigId;\n if (this.#config.model) {\n this.#resolvedModelConfigId = await this.#config.client.resolveModelConfigId(\n this.#config.model,\n signal,\n );\n }\n this.#modelResolved = true;\n return this.#resolvedModelConfigId;\n }\n\n /**\n * Resolve a user message's content to chatd input parts, uploading any file\n * parts to chat-file storage (the upload endpoint needs only the organization\n * id, so this runs before the chat exists). Pre-uploaded files carried via\n * `providerOptions.coder.fileId` are referenced without re-uploading.\n */\n #buildContent(content: UserContent, signal?: AbortSignal): Promise<ChatInputPart[]> {\n const uploadFile: FilePartUploader = async (f) => {\n const uploaded = await this.#config.client.uploadChatFile(\n this.#config.organizationId,\n { content: dataContentToFileContent(f.data), mediaType: f.mediaType, name: f.filename },\n signal,\n );\n return uploaded.id;\n };\n return userContentToInputParts(content, uploadFile);\n }\n\n async *#runTurn(\n options: LanguageModelV4CallOptions,\n ): AsyncGenerator<LanguageModelV4StreamPart, void, void> {\n // A single model instance owns one chatd session's mutable state, so it is\n // single-flight: reject overlapping turns rather than silently corrupting\n // chatId / lastSeenMessageId / submitted tool-call tracking.\n if (this.#inFlight) {\n throw new CoderAgentError(\n \"A generation is already in flight on this CoderAgent (single-flight). Use a separate CoderAgent instance for concurrent sessions.\",\n );\n }\n this.#inFlight = true;\n\n // Combine the caller's abort signal with an optional per-turn timeout into a\n // single signal (the platform composes and cleans these up for us). When\n // there's neither a caller signal nor a timeout, `signal` stays undefined and\n // there is no per-turn setup. Keep a reference to our *own* timeout signal so\n // a timeout stays distinguishable from a caller abort — even when the caller's\n // own signal is itself an `AbortSignal.timeout` (whose reason is a TimeoutError\n // too, so reason-sniffing alone would misclassify it).\n const externalSignal = options.abortSignal;\n const timeoutMs = this.#config.requestTimeoutMs;\n const timeoutSignal =\n timeoutMs !== undefined && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;\n const sources = [externalSignal, timeoutSignal].filter(\n (s): s is AbortSignal => s !== undefined,\n );\n const signal: AbortSignal | undefined =\n sources.length > 0 ? AbortSignal.any(sources) : undefined;\n\n // Translator is hoisted so `finally` can read whether the turn settled.\n const translator = new TurnTranslator({ dynamicToolNames: dynamicToolNames(options.tools) });\n\n // Interrupting the *server* run (not just closing the WebSocket) is what frees\n // the chat's workspace/resources. Fire it at most once — on abort/timeout, and\n // on teardown of an unsettled turn (see `finally`), which also covers stream\n // `cancel()` and premature close, neither of which aborts the signal. A chat\n // whose id we never received (createChat aborted mid-flight) can't be reached.\n let interruptSent = false;\n const interrupt = (): void => {\n const id = this.#chatId;\n if (interruptSent || !id) return;\n interruptSent = true;\n void this.#config.client.interruptChat(id).catch(() => {});\n };\n signal?.addEventListener(\"abort\", interrupt, { once: true });\n\n // Map an abort of the combined signal to the right error. Our own\n // `timeoutSignal` having fired means a per-turn timeout; otherwise it's a\n // caller abort, re-thrown as the caller's reason (preserving AbortError so the\n // AI SDK still recognizes the cancellation).\n const throwIfAborted = (): void => {\n if (timeoutSignal?.aborted) {\n throw new CoderChatError({\n message: `Coder Agent turn exceeded its ${timeoutMs}ms requestTimeoutMs budget.`,\n kind: \"timeout\",\n retryable: true,\n });\n }\n if (signal?.aborted) {\n throw (\n externalSignal?.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\")\n );\n }\n };\n\n try {\n const { prompt } = options;\n\n // chatd does not constrain output to a JSON schema server-side, so a\n // `responseFormat: json` request can't be honored. Warn rather than\n // silently mislead — schema-constrained output should go through the\n // provider (@coder/ai-sdk-provider) instead.\n const warnings: SharedV4Warning[] = [];\n if (options.responseFormat?.type === \"json\") {\n warnings.push({\n type: \"unsupported\",\n feature: \"responseFormat\",\n details:\n \"Coder Agents does not enforce a JSON schema server-side, so structured output is best-effort (not schema-constrained). For reliable structured output, use @coder/ai-sdk-provider (createCoder) with generateObject / Output.object.\",\n });\n }\n yield { type: \"stream-start\", warnings };\n\n const action = classifyTurnAction(prompt);\n if (action.kind === \"noop\") {\n throw new CoderAgentError(\n \"CoderAgent received a prompt with no user message or tool results to act on.\",\n );\n }\n\n let afterId: number | undefined;\n\n if (action.kind === \"new-turn\") {\n // Resolve the model config and upload any file parts concurrently — they\n // are independent round-trips. (Uploads run before the chat exists and\n // resolve file parts to `file` input parts referencing their uploaded ids.)\n const [modelConfigId, content] = await Promise.all([\n this.#resolveModelConfigId(signal),\n this.#buildContent(action.content, signal),\n ]);\n if (!this.#chatId) {\n const req: CreateChatRequest = {\n organization_id: this.#config.organizationId,\n content,\n client_type: \"api\",\n };\n const system = extractSystemPrompt(prompt);\n if (system) req.system_prompt = system;\n const tools = toolsToDynamicTools(options.tools);\n if (tools.length > 0) req.unsafe_dynamic_tools = tools;\n if (modelConfigId) req.model_config_id = modelConfigId;\n if (this.#config.workspaceId) req.workspace_id = this.#config.workspaceId;\n if (this.#config.mcpServerIds?.length) req.mcp_server_ids = this.#config.mcpServerIds;\n if (this.#config.planMode) req.plan_mode = this.#config.planMode;\n const chat = await this.#config.client.createChat(req, signal);\n this.#chatId = chat.id;\n afterId = this.#lastSeenMessageId > 0 ? this.#lastSeenMessageId : undefined;\n } else {\n const resp = await this.#config.client.createChatMessage(\n this.#chatId,\n {\n content,\n ...(modelConfigId ? { model_config_id: modelConfigId } : {}),\n },\n signal,\n );\n afterId = resp.message?.id ?? this.#lastSeenMessageId;\n }\n } else {\n // resume\n if (!this.#chatId)\n throw new CoderChatError({ message: \"cannot submit tool results before a chat exists\" });\n const fresh = action.toolResults.filter(\n (r) => !this.#submittedToolCallIds.has(r.tool_call_id),\n );\n if (fresh.length > 0) {\n await this.#config.client.submitToolResults(this.#chatId, { results: fresh }, signal);\n for (const r of fresh) this.#submittedToolCallIds.add(r.tool_call_id);\n }\n afterId = this.#lastSeenMessageId;\n }\n\n const chatId = this.#chatId as string;\n // chatd emits the `requires_action` status BEFORE the `action_required`\n // event that carries the pending tool calls, so for that status we keep\n // reading until the client tool calls have actually been emitted (bounded\n // by a safety counter, since the stream is a live subscription).\n let sinceRequiresAction = 0;\n try {\n for await (const ev of this.#config.client.streamEvents(chatId, { afterId, signal })) {\n for (const part of translator.ingest(ev)) yield part;\n const status = translator.terminalStatus;\n if (status) {\n if (status !== \"requires_action\") break;\n if (translator.clientToolCallSeen) break;\n if (++sinceRequiresAction > 200) break;\n }\n }\n } catch (err) {\n // Abort surfaces here only if the reader threw instead of closing cleanly;\n // prefer the abort/timeout classification.\n throwIfAborted();\n // The reader only throws transport-level CoderAgentErrors (socket error /\n // unparseable frame). Surface them as a retryable stream failure so a\n // caller's `CoderChatError && retryable` retry path catches a dropped\n // connection instead of seeing a bare, non-retryable error.\n if (\n err instanceof CoderAgentError &&\n !(err instanceof CoderApiError) &&\n !(err instanceof CoderChatError)\n ) {\n throw new CoderChatError({\n message: `Coder chat stream failed mid-turn: ${err.message}`,\n kind: \"stream_closed\",\n retryable: true,\n });\n }\n throw err;\n }\n\n // The stream loop exits cleanly when the socket closes on abort, so classify\n // an abort/timeout here before treating the end as a normal/closed turn.\n throwIfAborted();\n\n // No terminal status: the stream ended before the turn settled. If an\n // `error` event arrived (without a trailing `status: error`), fall through to\n // finish() so the real error surfaces (unified:\"error\" + the error part),\n // consistent with the `status: error` path; otherwise it's a genuine\n // premature close — surface it rather than a clean (truncated) `stop`.\n if (!translator.terminalStatus && !translator.error) {\n throw new CoderChatError({\n message:\n \"Coder chat stream ended before the turn settled (connection closed or the server ended the stream without a terminal status).\",\n kind: \"stream_closed\",\n retryable: true,\n });\n }\n\n for (const part of translator.finish()) yield part;\n } catch (err) {\n // A timeout/caller abort during the REST phase (createChat / message /\n // tool-results / model resolution) rejects the fetch before the stream loop;\n // reclassify so the documented retryable timeout/abort contract still holds.\n throwIfAborted();\n throw err;\n } finally {\n // Advance the cursor on every exit (success, abort, error) so resuming the\n // same chat doesn't re-read messages already streamed this turn.\n if (translator.maxMessageId > this.#lastSeenMessageId)\n this.#lastSeenMessageId = translator.maxMessageId;\n signal?.removeEventListener(\"abort\", interrupt);\n // Teardown of an unsettled turn — stream cancel(), premature close, or an\n // abort the listener didn't cover — interrupt the server so it stops.\n if (!translator.terminalStatus) interrupt();\n this.#inFlight = false;\n }\n }\n\n async doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult> {\n // A consumer can tear the stream down via ReadableStream.cancel() without\n // aborting options.abortSignal. Route cancel through an abort so the turn\n // interrupts the server run and the blocked stream reader unblocks — a bare\n // gen.return() would deadlock on a pending socket read and never reach the\n // interrupt, leaking the workspace.\n const cancelController = new AbortController();\n const abortSignal = options.abortSignal\n ? AbortSignal.any([options.abortSignal, cancelController.signal])\n : cancelController.signal;\n const gen = this.#runTurn({ ...options, abortSignal });\n const stream = new ReadableStream<LanguageModelV4StreamPart>({\n async pull(controller) {\n try {\n const { value, done } = await gen.next();\n if (done) controller.close();\n else controller.enqueue(value);\n } catch (err) {\n // A consumer-initiated cancel aborts the turn (to interrupt the server\n // and unblock the reader); that surfaces here as the turn's AbortError,\n // but it's an intentional teardown, so end the stream cleanly rather\n // than erroring it. A caller's own abortSignal still errors as usual.\n if (cancelController.signal.aborted) controller.close();\n else controller.error(err);\n }\n },\n async cancel() {\n cancelController.abort();\n await gen.return().catch(() => {});\n },\n });\n return { stream };\n }\n\n async doGenerate(options: LanguageModelV4CallOptions): Promise<LanguageModelV4GenerateResult> {\n const content: LanguageModelV4Content[] = [];\n const textBuf = new Map<string, string>();\n const reasoningBuf = new Map<string, string>();\n let usage: LanguageModelV4Usage = EMPTY_USAGE;\n let finishReason: LanguageModelV4GenerateResult[\"finishReason\"] = {\n unified: \"stop\",\n raw: undefined,\n };\n let providerMetadata: LanguageModelV4GenerateResult[\"providerMetadata\"];\n const warnings: LanguageModelV4GenerateResult[\"warnings\"] = [];\n\n for await (const part of this.#runTurn(options)) {\n switch (part.type) {\n case \"stream-start\":\n warnings.push(...part.warnings);\n break;\n case \"text-start\":\n textBuf.set(part.id, \"\");\n break;\n case \"text-delta\":\n textBuf.set(part.id, (textBuf.get(part.id) ?? \"\") + part.delta);\n break;\n case \"text-end\": {\n const t = textBuf.get(part.id) ?? \"\";\n if (t.length > 0) content.push({ type: \"text\", text: t });\n textBuf.delete(part.id);\n break;\n }\n case \"reasoning-start\":\n reasoningBuf.set(part.id, \"\");\n break;\n case \"reasoning-delta\":\n reasoningBuf.set(part.id, (reasoningBuf.get(part.id) ?? \"\") + part.delta);\n break;\n case \"reasoning-end\": {\n const t = reasoningBuf.get(part.id) ?? \"\";\n if (t.length > 0) content.push({ type: \"reasoning\", text: t });\n reasoningBuf.delete(part.id);\n break;\n }\n case \"tool-call\":\n case \"tool-result\":\n case \"source\":\n case \"file\":\n content.push(part);\n break;\n case \"finish\":\n usage = part.usage;\n finishReason = part.finishReason;\n providerMetadata = part.providerMetadata;\n break;\n case \"error\":\n throw part.error instanceof Error\n ? part.error\n : new CoderChatError({ message: String(part.error) });\n default:\n break;\n }\n }\n\n return {\n content,\n finishReason,\n usage,\n warnings,\n ...(providerMetadata ? { providerMetadata } : {}),\n };\n }\n}\n","import type { DynamicToolUIPart, UIMessage } from \"ai\";\nimport type { ChatMessage, ChatMessagePart } from \"../coder/types.js\";\n\n/** Options for {@link chatMessagesToUIMessages}. */\nexport interface ChatMessagesToUIMessagesOptions {\n /**\n * Resolves a persisted `file` part to a URL for its `FileUIPart`. Persisted\n * file parts carry only a `file_id` (no bytes, and usually no `url`), so\n * without a URL there is nothing for the UI to render and the part is\n * skipped. Download the bytes with `CoderChatClient.getChatFile(fileId)`\n * and return a data:/object/proxy URL here to keep attachments visible.\n * Returning `undefined` falls back to the part's own `url`, if any.\n */\n fileUrl?: (part: ChatMessagePart) => string | undefined;\n}\n\nfunction toolErrorText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (result === undefined || result === null) return \"\";\n return JSON.stringify(result) ?? \"\";\n}\n\nfunction toDynamicToolPart(\n call: ChatMessagePart,\n result: ChatMessagePart | undefined,\n): DynamicToolUIPart | undefined {\n const toolCallId = call.tool_call_id;\n const toolName = call.tool_name;\n if (!toolCallId || !toolName) return undefined;\n const providerExecuted = call.provider_executed ?? result?.provider_executed;\n const base = {\n type: \"dynamic-tool\" as const,\n toolName,\n toolCallId,\n ...(providerExecuted !== undefined ? { providerExecuted } : {}),\n };\n if (!result) return { ...base, state: \"input-available\", input: call.args };\n if (result.is_error) {\n return {\n ...base,\n state: \"output-error\",\n input: call.args,\n errorText: toolErrorText(result.result),\n };\n }\n return { ...base, state: \"output-available\", input: call.args, output: result.result };\n}\n\nfunction toUIParts(\n content: ChatMessagePart[],\n resultsByCallId: ReadonlyMap<string, ChatMessagePart>,\n opts: ChatMessagesToUIMessagesOptions | undefined,\n): UIMessage[\"parts\"] {\n const parts: UIMessage[\"parts\"] = [];\n for (const part of content) {\n switch (part.type) {\n case \"text\":\n parts.push({ type: \"text\", text: part.text ?? \"\", state: \"done\" });\n break;\n case \"reasoning\":\n parts.push({ type: \"reasoning\", text: part.text ?? \"\", state: \"done\" });\n break;\n case \"tool-call\": {\n const result = part.tool_call_id ? resultsByCallId.get(part.tool_call_id) : undefined;\n const ui = toDynamicToolPart(part, result);\n if (ui) parts.push(ui);\n break;\n }\n case \"source\":\n // `sourceId` is required by the AI SDK; fall back to the URL when the\n // wire part has no `source_id`. Without a URL there is nothing to\n // link, so the part is skipped.\n if (part.url) {\n parts.push({\n type: \"source-url\",\n sourceId: part.source_id || part.url,\n url: part.url,\n ...(part.title ? { title: part.title } : {}),\n });\n }\n break;\n case \"file\": {\n const url = opts?.fileUrl?.(part) ?? part.url;\n if (url) {\n parts.push({\n type: \"file\",\n mediaType: part.media_type ?? \"application/octet-stream\",\n ...(part.name ? { filename: part.name } : {}),\n url,\n });\n }\n break;\n }\n case \"tool-result\":\n // Folded into the originating `dynamic-tool` part above.\n break;\n default:\n // file-reference / context-file / skill / future kinds: no UI\n // equivalent; skip so history written by newer Coder servers\n // degrades gracefully.\n break;\n }\n }\n return parts;\n}\n\n/**\n * Converts persisted chat history (`CoderChatClient.getMessages`) into AI SDK\n * v6 `UIMessage`s, e.g. to rehydrate `useChat({ messages })` for an existing\n * chat. Pure and side-effect free.\n *\n * The mapping mirrors what a live-streamed transcript of the same turn looks\n * like, with one caveat around client-tool typing (below):\n * - One `ChatMessage` becomes one `UIMessage` (ids stringified), sorted\n * chronologically by id regardless of input order — the messages endpoint\n * pages newest-first by default, while `useChat` needs oldest-first. The\n * exception is `role: \"tool\"` messages: their `tool-result` parts are\n * folded into the originating assistant `dynamic-tool` part and the message\n * itself is dropped.\n * - `text`/`reasoning` parts map to their UI counterparts with\n * `state: \"done\"` (history is complete, never streaming).\n * - Every `tool-call` part becomes a `dynamic-tool` part, in state\n * `\"output-available\"` once a result exists (`\"output-error\"` when it is\n * marked `is_error`), or `\"input-available\"` while no result has been\n * persisted yet. For server-executed tools this matches the live stream\n * (they stream with `dynamic: true`); client (`ToolSet`) tools stream live\n * as statically-typed `tool-{name}` parts, but persisted history does not\n * record which names were client tools, so they rehydrate as `dynamic-tool`\n * too. Render tools by name — ai's `isToolOrDynamicToolUIPart` +\n * `getToolOrDynamicToolName` — rather than exact `part.type` matches and\n * both shapes look identical.\n * - `source` parts become `source-url` parts; skipped when the wire part has\n * no `url`.\n * - `file` parts become `file` UI parts only when a URL is available (see\n * {@link ChatMessagesToUIMessagesOptions.fileUrl}): persisted file parts\n * carry only a `file_id`, and `FileUIPart` requires a `url`. Parts without\n * one are skipped.\n * - `file-reference` / `context-file` / `skill` / unknown part kinds have no\n * UI equivalent and are skipped silently (forward compatible).\n */\nexport function chatMessagesToUIMessages(\n messages: ChatMessage[],\n opts?: ChatMessagesToUIMessagesOptions,\n): UIMessage[] {\n // Normalize to chronological order (ids are per-chat sequential). The\n // messages endpoint pages newest-first by default (only `after_id` queries\n // return ascending), and `useChat` expects oldest-first — accepting any\n // input order makes both pagination shapes safe to pass straight in.\n const ordered = [...messages].sort((a, b) => a.id - b.id);\n\n // Pass 1: index tool results by call id. chatd persists results as separate\n // role:\"tool\" messages; scan every message so results inlined on assistant\n // snapshots fold identically.\n const resultsByCallId = new Map<string, ChatMessagePart>();\n for (const message of ordered) {\n for (const part of message.content ?? []) {\n if (part.type !== \"tool-result\" || !part.tool_call_id) continue;\n if (!resultsByCallId.has(part.tool_call_id)) resultsByCallId.set(part.tool_call_id, part);\n }\n }\n\n // Pass 2: one UIMessage per ChatMessage, chronological. role:\"tool\" messages\n // were folded above and produce none.\n const out: UIMessage[] = [];\n for (const message of ordered) {\n if (message.role === \"tool\") continue;\n out.push({\n id: String(message.id),\n role: message.role,\n parts: toUIParts(message.content ?? [], resultsByCallId, opts),\n });\n }\n return out;\n}\n"],"mappings":"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","names":["sleep"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent/coder-agent.ts","../src/errors.ts","../src/ai-version.ts","../src/files.ts","../src/coder/types.ts","../src/coder/ws.ts","../src/coder/client.ts","../src/coder/workspaces.ts","../src/model/prompt.ts","../src/model/translate.ts","../src/model/language-model.ts","../src/model/history.ts"],"sourcesContent":["import {\n type Agent,\n type FilePart,\n isStepCount,\n type StopCondition,\n type SystemModelMessage,\n type ToolChoice,\n ToolLoopAgent,\n type ToolLoopAgentSettings,\n type ToolSet,\n} from \"ai\";\nimport { assertSupportedAiVersion } from \"../ai-version.js\";\nimport {\n type ChatFileInput,\n CoderChatClient,\n type CoderChatClientOptions,\n type UploadedChatFile,\n} from \"../coder/client.js\";\nimport type { ChatModelConfig } from \"../coder/types.js\";\nimport {\n type PreviewShareLevel,\n resolveWorkspacePreview,\n shareWorkspacePreview,\n type WorkspaceApiConnection,\n} from \"../coder/workspaces.js\";\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport type { FileContent } from \"../files.js\";\nimport { CoderLanguageModel } from \"../model/language-model.js\";\nimport { CODER_PROVIDER_OPTIONS } from \"../model/prompt.js\";\nimport type { WorkspaceFileStore, WorkspacePlacement } from \"../workspace-files.js\";\n\ntype InnerAgent<TOOLS extends ToolSet> = ToolLoopAgent<never, TOOLS>;\n\n/** A handle to a file uploaded as a chat attachment (see {@link CoderAgent.attach}). */\nexport interface ChatAttachment extends UploadedChatFile {\n /**\n * A native AI SDK `file` part that references this upload by id. Drop it into\n * a user message's `content` to reuse the file across turns without\n * re-uploading — the agent recognizes the id and skips the upload.\n *\n * The reference travels in `providerOptions.coder.fileId`; if you persist the\n * part through a store that strips `providerOptions`, the reference is lost\n * (re-`attach()` instead of persisting the handle).\n */\n toFilePart(): FilePart;\n}\n\nfunction makeChatAttachment(file: UploadedChatFile): ChatAttachment {\n return {\n ...file,\n toFilePart: () => ({\n type: \"file\",\n // Bytes are unused: the reference travels in providerOptions.coder.fileId.\n data: \"\",\n mediaType: file.mediaType,\n ...(file.name ? { filename: file.name } : {}),\n providerOptions: { [CODER_PROVIDER_OPTIONS]: { fileId: file.id } },\n }),\n };\n}\n\n/** Default step ceiling. Each \"step\" is one client-tool round-trip; chatd caps\n * its own server-side loop at 1200 steps independently. */\nconst DEFAULT_STOP = isStepCount(64);\n\n/**\n * Bounded-cleanup defaults. An interrupted chat keeps winding down server-side\n * for a few seconds, during which archiving 409s — {@link CoderAgent.archive}\n * and `[Symbol.asyncDispose]` retry those 409s every ~`SETTLE_RETRY_DELAY_MS`\n * for up to ~`SETTLE_DEADLINE_MS` overall before giving up.\n */\nconst SETTLE_DEADLINE_MS = 15_000;\nconst SETTLE_RETRY_DELAY_MS = 1_000;\n\n/**\n * Clamps a user-supplied delay to what `AbortSignal.timeout`/`setTimeout`\n * accept: a non-negative integer no larger than the 32-bit timer cap.\n * Anything else (negative, fractional beyond flooring, `NaN`, `Infinity`)\n * falls back to the default rather than throwing at dispose time.\n */\nfunction sanitizeDelayMs(value: number | undefined, fallback: number): number {\n const MAX_TIMER_MS = 2 ** 31 - 1;\n if (value === undefined || Number.isNaN(value)) return fallback;\n if (value === Number.POSITIVE_INFINITY) return MAX_TIMER_MS;\n if (!Number.isFinite(value) || value < 0) return fallback;\n return Math.min(Math.floor(value), MAX_TIMER_MS);\n}\n\n/** Abort-aware sleep: resolves after `ms`, or rejects with the signal's reason. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Archive a chat, retrying while it is still settling. Interrupting a run does\n * not stop it instantly — chatd winds it down for a few more seconds and 409s\n * archive attempts in that window. Only those 409s are retried (other failures\n * and caller aborts rethrow immediately); once `deadlineMs` has elapsed the\n * last 409 is rethrown.\n */\nasync function archiveWhenSettled(\n client: CoderChatClient,\n chatId: string,\n opts: { deadlineMs: number; retryDelayMs: number; signal?: AbortSignal },\n): Promise<void> {\n // The deadline decides when to stop retrying; the timeout signal additionally\n // unsticks an archive request that hangs (both track the same clock).\n const timeout = AbortSignal.timeout(opts.deadlineMs);\n const signal = opts.signal ? AbortSignal.any([opts.signal, timeout]) : timeout;\n const deadline = Date.now() + opts.deadlineMs;\n for (;;) {\n try {\n await client.archiveChat(chatId, signal);\n return;\n } catch (err) {\n if (!(err instanceof CoderApiError) || err.status !== 409) throw err;\n // Stop when there is no budget left for another pause + attempt, surfacing\n // the 409 (the actionable error) rather than an opaque timeout.\n if (Date.now() + opts.retryDelayMs > deadline) throw err;\n // Pause on the caller's signal only — the deadline is enforced above.\n await sleep(opts.retryDelayMs, opts.signal);\n }\n }\n}\n\nexport interface CoderAgentSettings<TOOLS extends ToolSet = {}> {\n // --- connection (provide a client, or baseUrl + token) ---\n /**\n * A pre-built {@link CoderChatClient}, used instead of constructing one from\n * `baseUrl`/`token`. The preview helpers ({@link CoderAgent.getPreview},\n * {@link CoderAgent.sharePreview}) call non-chat endpoints and therefore\n * still need credentials — pass `baseUrl` + `token` alongside `client` to\n * enable them.\n */\n client?: CoderChatClient;\n /** Coder deployment base URL, e.g. `https://dev.coder.com`. */\n baseUrl?: string;\n /** Coder API/session token (sent as `Coder-Session-Token`). */\n token?: string;\n fetch?: CoderChatClientOptions[\"fetch\"];\n webSocketFactory?: CoderChatClientOptions[\"webSocketFactory\"];\n\n // --- Coder chat configuration ---\n /** Organization UUID that owns the chat (required). */\n organizationId: string;\n /** Model hint: UUID, `provider:model`, model id, or display-name substring. */\n model?: string;\n /** Bind the chat to a Coder workspace (enables workspace-scoped tools). */\n workspaceId?: string;\n /**\n * Adapter for writing files onto a workspace filesystem, enabling\n * {@link CoderAgent.uploadToWorkspace}. Supply one backed by a workspace\n * connection (e.g. a `@coder/ai-sdk-eve-sandbox` session).\n */\n workspaceFiles?: WorkspaceFileStore;\n /** chatd-side MCP servers to enable for this chat. */\n mcpServerIds?: string[];\n /** chatd plan mode. */\n planMode?: \"\" | \"plan\";\n /** Resume an existing chat (session) instead of creating a new one. */\n chatId?: string;\n\n // --- agent configuration ---\n /** Stable agent id (exposed as {@link CoderAgent.id}). */\n id?: string;\n /** System instructions → chatd `system_prompt`. */\n instructions?: string | SystemModelMessage | Array<SystemModelMessage>;\n /** Custom (client-executed) tools. Each should have an `execute` for scripting. */\n tools?: TOOLS;\n /**\n * Per-tool context values for tools that declare a `contextSchema`, keyed by\n * tool name (forwarded to the AI SDK loop). Required by the SDK at runtime\n * whenever a supplied tool declares contextual data.\n */\n toolsContext?: ToolLoopAgentSettings<never, TOOLS>[\"toolsContext\"];\n toolChoice?: ToolChoice<NoInfer<TOOLS>>;\n /** Stop condition(s) for the AI SDK loop. Default `isStepCount(64)`. */\n stopWhen?: StopCondition<NoInfer<TOOLS>> | Array<StopCondition<NoInfer<TOOLS>>>;\n /**\n * SDK-level retries. Default `0`: this agent owns server-side chat state, so\n * SDK retries could duplicate a turn. Override with care.\n */\n maxRetries?: number;\n /**\n * Per-segment time budget in milliseconds, applied to each model round-trip\n * (chat creation / message / tool-result submission plus the server run until\n * it settles). If a segment runs longer (e.g. the server is wedged or a\n * workspace can't be scheduled), the run is interrupted server-side and the\n * call rejects with a retryable {@link CoderChatError} (`kind: \"timeout\"`)\n * instead of hanging. A multi-step `generate()` driving client tools makes\n * several segments, so this bounds each one — to cap total wall-clock for the\n * whole call, pass `abortSignal: AbortSignal.timeout(ms)`. Unset or\n * non-positive means no limit.\n */\n requestTimeoutMs?: number;\n /**\n * Overall deadline in milliseconds for bounded cleanup: how long\n * {@link CoderAgent.archive} keeps retrying while a freshly interrupted chat\n * settles server-side, and how long `[Symbol.asyncDispose]` may take in\n * total. Default 15 000. Primarily a test/tuning knob.\n */\n settleDeadlineMs?: number;\n /**\n * Pause in milliseconds between {@link CoderAgent.archive} retries while the\n * chat settles. Default 1000. Primarily a test/tuning knob.\n */\n settleRetryDelayMs?: number;\n}\n\n/** Options for {@link CoderAgent.getPreview}. */\nexport interface WorkspacePreviewOptions {\n /** Workspace port the app listens on (4–5 digit ports parse most reliably). */\n port: number;\n /**\n * Agent that serves the port. Optional when the workspace has exactly one\n * agent; required when it has several (the error lists the candidates).\n */\n agentName?: string;\n /**\n * Protocol the app speaks *inside the workspace* on that port — `https` adds\n * the `s` suffix (`3000s--…`) so the proxy connects over TLS. This is not\n * the browser scheme, which follows the deployment's access URL.\n * Default `\"http\"`.\n */\n protocol?: \"http\" | \"https\";\n signal?: AbortSignal;\n}\n\n/** A resolved workspace preview URL (see {@link CoderAgent.getPreview}). */\nexport interface WorkspacePreview {\n /** Browser-openable subdomain app URL, e.g. `https://3000--main--dev--alice.apps.example.com`. */\n url: string;\n}\n\n/** Options for {@link CoderAgent.sharePreview}. */\nexport interface SharePreviewOptions extends WorkspacePreviewOptions {\n /**\n * Who may open the preview: `authenticated` (any logged-in user; the\n * default), `organization` (members of the workspace's organization;\n * requires a newer Coder server), or `public` (no auth at all — mind what\n * the port serves).\n */\n shareLevel?: PreviewShareLevel;\n}\n\n/** A shared workspace preview (see {@link CoderAgent.sharePreview}). */\nexport interface SharedWorkspacePreview extends WorkspacePreview {\n /** The share level now in effect for the port. */\n shareLevel: PreviewShareLevel;\n}\n\n/**\n * An AI SDK-compliant agent backed by a remote Coder `chatd` agent runtime.\n *\n * `new CoderAgent({ ... })` returns an object that implements the AI SDK\n * `Agent` interface (`generate()`/`stream()`), so it composes with the rest of\n * the AI SDK ecosystem. Internally it wraps a {@link ToolLoopAgent} whose model\n * is a {@link CoderLanguageModel}; chatd runs the actual agent loop server-side.\n *\n * One `CoderAgent` instance corresponds to one chatd chat (\"session\"): the chat\n * is created on the first `generate()`/`stream()` and reused for subsequent\n * turns. Use {@link CoderAgent.resetSession} to start a fresh chat.\n */\nexport class CoderAgent<TOOLS extends ToolSet = {}> implements Agent<never, TOOLS> {\n readonly version = \"agent-v1\" as const;\n\n readonly #client: CoderChatClient;\n readonly #model: CoderLanguageModel;\n readonly #inner: InnerAgent<TOOLS>;\n readonly #organizationId: string;\n readonly #workspaceFiles: WorkspaceFileStore | undefined;\n readonly #workspaceId: string | undefined;\n readonly #workspaceApi: WorkspaceApiConnection | undefined;\n readonly #settleDeadlineMs: number;\n readonly #settleRetryDelayMs: number;\n\n constructor(settings: CoderAgentSettings<TOOLS>) {\n // Fail fast on an incompatible AI SDK major (see peer dependency `ai@^7`).\n assertSupportedAiVersion();\n\n if (settings.client) {\n this.#client = settings.client;\n } else if (settings.baseUrl && settings.token) {\n this.#client = new CoderChatClient({\n baseUrl: settings.baseUrl,\n token: settings.token,\n fetch: settings.fetch,\n webSocketFactory: settings.webSocketFactory,\n });\n } else {\n throw new CoderAgentError(\n \"CoderAgent requires either `client` or both `baseUrl` and `token`.\",\n );\n }\n\n this.#organizationId = settings.organizationId;\n this.#workspaceFiles = settings.workspaceFiles;\n this.#workspaceId = settings.workspaceId;\n // The preview helpers call stable v2 endpoints that CoderChatClient (chat\n // scoped) does not expose, so they need the raw credentials when given.\n this.#workspaceApi =\n settings.baseUrl && settings.token\n ? { baseUrl: settings.baseUrl, token: settings.token, fetch: settings.fetch }\n : undefined;\n // Sanitized so `AbortSignal.timeout` (which rejects non-integer, negative,\n // and non-finite delays with a RangeError) can never make disposal throw.\n this.#settleDeadlineMs = sanitizeDelayMs(settings.settleDeadlineMs, SETTLE_DEADLINE_MS);\n this.#settleRetryDelayMs = sanitizeDelayMs(settings.settleRetryDelayMs, SETTLE_RETRY_DELAY_MS);\n\n // A file written to one workspace isn't visible to a chat bound to another.\n if (\n settings.workspaceFiles &&\n settings.workspaceId &&\n settings.workspaceFiles.workspaceId !== settings.workspaceId\n ) {\n throw new CoderAgentError(\n `workspaceFiles.workspaceId (${settings.workspaceFiles.workspaceId}) does not match the ` +\n `agent's workspaceId (${settings.workspaceId}); the chat's tools would not see uploaded files.`,\n );\n }\n\n this.#model = new CoderLanguageModel({\n client: this.#client,\n organizationId: settings.organizationId,\n model: settings.model,\n workspaceId: settings.workspaceId,\n mcpServerIds: settings.mcpServerIds,\n planMode: settings.planMode,\n chatId: settings.chatId,\n requestTimeoutMs: settings.requestTimeoutMs,\n });\n\n // Whether `toolsContext` is required depends on the concrete TOOLS the\n // caller supplies (tools with a `contextSchema`), a conditional TypeScript\n // cannot resolve for a generic TOOLS — hence the cast. The field itself is\n // typed on CoderAgentSettings, so callers still get the checked shape.\n this.#inner = new ToolLoopAgent<never, TOOLS>({\n model: this.#model,\n id: settings.id,\n instructions: settings.instructions,\n tools: settings.tools,\n toolsContext: settings.toolsContext,\n toolChoice: settings.toolChoice,\n stopWhen: settings.stopWhen ?? DEFAULT_STOP,\n maxRetries: settings.maxRetries ?? 0,\n } as ToolLoopAgentSettings<never, TOOLS>);\n }\n\n get id(): string | undefined {\n return this.#inner.id;\n }\n\n get tools(): TOOLS {\n return this.#inner.tools;\n }\n\n /** The underlying chatd client. */\n get client(): CoderChatClient {\n return this.#client;\n }\n\n /** The current chatd chat id, once a turn has started. */\n get chatId(): string | undefined {\n return this.#model.chatId;\n }\n\n generate(\n options: Parameters<InnerAgent<TOOLS>[\"generate\"]>[0],\n ): ReturnType<InnerAgent<TOOLS>[\"generate\"]> {\n return this.#inner.generate(options);\n }\n\n stream(\n options: Parameters<InnerAgent<TOOLS>[\"stream\"]>[0],\n ): ReturnType<InnerAgent<TOOLS>[\"stream\"]> {\n return this.#inner.stream(options);\n }\n\n // --- session helpers ------------------------------------------------------\n\n /**\n * List the model configs available on the deployment. Use this to discover\n * valid values for the `model` hint instead of guessing ids — match on\n * `id`, `provider`/`model`, or `display_name`.\n */\n listModels(signal?: AbortSignal): Promise<ChatModelConfig[]> {\n return this.#client.listModelConfigs(signal);\n }\n\n /** Start a fresh chatd chat on the next turn. */\n resetSession(): void {\n this.#model.resetSession();\n }\n\n /**\n * Interrupt the in-flight generation, if any. Resolves as soon as the server\n * acknowledges the interrupt — the run keeps winding down asynchronously for\n * a few seconds afterwards (see {@link CoderAgent.archive}).\n */\n async interrupt(opts?: { signal?: AbortSignal }): Promise<void> {\n const id = this.#model.chatId;\n if (id) await this.#client.interruptChat(id, opts?.signal);\n }\n\n /**\n * Archive the underlying chat (safe cleanup; hides it from listings).\n *\n * A freshly interrupted/settled chat can keep winding down server-side for a\n * few seconds, during which the server rejects archiving with a 409. Those\n * 409s are retried (~1s apart, ~15s overall — see `settleDeadlineMs` /\n * `settleRetryDelayMs`) and the last one is rethrown if the chat never\n * settles; any other failure, including a caller abort, rethrows immediately.\n */\n async archive(opts?: { signal?: AbortSignal }): Promise<void> {\n const id = this.#model.chatId;\n if (!id) return;\n await archiveWhenSettled(this.#client, id, {\n deadlineMs: this.#settleDeadlineMs,\n retryDelayMs: this.#settleRetryDelayMs,\n signal: opts?.signal,\n });\n }\n\n /**\n * Clean up the chat when the agent leaves an `await using` scope, so cleanup\n * rides scope exit instead of a separate call you have to remember in a\n * `finally`. Interrupts any in-flight server run, then archives the chat\n * (retrying while the interrupted run settles). Best-effort and bounded\n * (~15s overall): disposal errors are swallowed after the bounded attempts\n * so they can't mask the scope's own error — call {@link CoderAgent.archive}\n * directly when you need guaranteed cleanup.\n *\n * @example\n * ```ts\n * await using agent = new CoderAgent({ ... });\n * const { text } = await agent.generate({ prompt: \"…\" });\n * // agent.interrupt() + agent.archive() run automatically here.\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n // archive() only soft-hides the chat; it does not stop a generation. If the\n // scope exits mid-turn (e.g. generate() threw, or an early return), interrupt\n // first so chatd stops generating and releases the workspace, then archive.\n // One shared timeout bounds both calls so scope exit can never hang.\n const deadline = AbortSignal.timeout(this.#settleDeadlineMs);\n try {\n await this.interrupt({ signal: deadline });\n } catch {\n /* best-effort cleanup */\n }\n try {\n await this.archive({ signal: deadline });\n } catch {\n /* best-effort cleanup */\n }\n }\n\n // --- files ----------------------------------------------------------------\n\n /**\n * Upload a file as a chat attachment — content for the model to *read* (a PDF,\n * image, CSV, …). Returns a handle you can reference from a later turn via\n * {@link ChatAttachment.toFilePart} (no re-upload). Validated against the chat\n * allowlist and 10 MiB cap up front.\n *\n * You usually don't need this: a native AI SDK `file` part dropped into a\n * message's `content` is uploaded transparently. Reach for `attach()` to\n * pre-upload, to reuse one file across turns, or to validate before sending.\n *\n * For large or non-allowlisted files (zips, datasets, binaries) — material for\n * the agent to *operate on* — use {@link CoderAgent.uploadToWorkspace}.\n */\n async attach(file: ChatFileInput, signal?: AbortSignal): Promise<ChatAttachment> {\n const uploaded = await this.#client.uploadChatFile(this.#organizationId, file, signal);\n return makeChatAttachment(uploaded);\n }\n\n /**\n * Write a file onto the bound workspace's filesystem for the agent to operate\n * on (extract, build, read with its tools). Requires a {@link WorkspaceFileStore}\n * via the `workspaceFiles` setting. The file is written as-is — no unpacking;\n * instruct the agent to `unzip`/`tar -x`, or extract over your own connection.\n *\n * The chat should be bound to the same workspace (`workspaceId`) so the agent's\n * server-side tools can see the file.\n */\n async uploadToWorkspace(file: {\n content: FileContent;\n /** Destination path inside the workspace (the adapter may resolve relatives). */\n path: string;\n signal?: AbortSignal;\n }): Promise<WorkspacePlacement> {\n if (!this.#workspaceFiles) {\n throw new CoderAgentError(\n \"uploadToWorkspace requires a `workspaceFiles` adapter. Construct the agent with \" +\n \"`workspaceFiles` (e.g. backed by a @coder/ai-sdk-eve-sandbox session) to enable it.\",\n );\n }\n const { path } = await this.#workspaceFiles.write({\n content: file.content,\n path: file.path,\n signal: file.signal,\n });\n return { workspaceId: this.#workspaceFiles.workspaceId, path };\n }\n\n // --- previews ---------------------------------------------------------------\n\n /** The v2 connection + workspace id the preview helpers need, or a clear error. */\n #previewContext(method: string): { conn: WorkspaceApiConnection; workspaceId: string } {\n if (!this.#workspaceId) {\n throw new CoderAgentError(\n `${method}() requires the agent to be bound to a workspace — construct the CoderAgent ` +\n `with the \\`workspaceId\\` setting.`,\n );\n }\n if (!this.#workspaceApi) {\n throw new CoderAgentError(\n `${method}() needs the deployment's REST credentials — construct the CoderAgent with ` +\n `\\`baseUrl\\` and \\`token\\` (they can accompany \\`client\\`).`,\n );\n }\n return { conn: this.#workspaceApi, workspaceId: this.#workspaceId };\n }\n\n /**\n * Resolve the browser URL where a port on the bound workspace can be\n * previewed, e.g. `https://3000--main--dev--alice.apps.example.com`.\n * Composed from stable v2 endpoints (workspace lookup + wildcard app host),\n * so it works against old Coder servers. Requires the `workspaceId` setting,\n * `baseUrl`/`token` credentials, and a deployment with a wildcard access URL\n * configured.\n *\n * The URL honors the port's current share level — private to the workspace\n * owner unless shared (see {@link CoderAgent.sharePreview}).\n */\n async getPreview(opts: WorkspacePreviewOptions): Promise<WorkspacePreview> {\n const { conn, workspaceId } = this.#previewContext(\"getPreview\");\n const { url } = await resolveWorkspacePreview(\n conn,\n { workspaceId, port: opts.port, agentName: opts.agentName, protocol: opts.protocol },\n opts.signal,\n );\n return { url };\n }\n\n /**\n * Share a port on the bound workspace and return its preview URL. Upserts\n * the port's share level — `authenticated` by default — so teammates (or,\n * with `public`, anyone) can open what the agent is serving; re-invoking\n * updates the level in place. On Coder servers that predate port sharing\n * (< v2.9) this fails with a 404 {@link CoderApiError} saying so.\n */\n async sharePreview(opts: SharePreviewOptions): Promise<SharedWorkspacePreview> {\n const { conn, workspaceId } = this.#previewContext(\"sharePreview\");\n return shareWorkspacePreview(\n conn,\n {\n workspaceId,\n port: opts.port,\n agentName: opts.agentName,\n protocol: opts.protocol,\n shareLevel: opts.shareLevel,\n },\n opts.signal,\n );\n }\n}\n","import type { ChatErrorPayload } from \"./coder/types.js\";\n\n/** Base error for all `@coder/ai-sdk-agent` failures. */\nexport class CoderAgentError extends Error {\n override name = \"CoderAgentError\";\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n }\n}\n\n/** An HTTP request to the Coder API failed. */\nexport class CoderApiError extends CoderAgentError {\n override name = \"CoderApiError\";\n readonly status: number;\n readonly detail: string | undefined;\n readonly method: string;\n readonly path: string;\n constructor(args: {\n status: number;\n method: string;\n path: string;\n message: string;\n detail?: string;\n }) {\n super(\n `Coder API ${args.method} ${args.path} failed (${args.status}): ${args.message}` +\n (args.detail ? ` — ${args.detail}` : \"\"),\n );\n this.status = args.status;\n this.detail = args.detail;\n this.method = args.method;\n this.path = args.path;\n }\n}\n\n/** A chat generation ended in an error status. */\nexport class CoderChatError extends CoderAgentError {\n override name = \"CoderChatError\";\n readonly kind: string | undefined;\n readonly provider: string | undefined;\n readonly retryable: boolean;\n readonly statusCode: number | undefined;\n constructor(payload: ChatErrorPayload) {\n super(payload.detail ? `${payload.message}: ${payload.detail}` : payload.message);\n this.kind = payload.kind;\n this.provider = payload.provider;\n this.retryable = payload.retryable ?? false;\n this.statusCode = payload.status_code;\n }\n}\n","import { CoderAgentError } from \"./errors.js\";\n\n/** The `ai` peer-dependency major this package is built against (`ai@^7`). */\nconst SUPPORTED_AI_MAJOR = 7;\n\n/**\n * Decides whether a resolved `ai` package version is supported. Returns the\n * error message to raise for a known-incompatible major, or `undefined` to\n * proceed. Unparseable strings also return `undefined`: the guard fails open\n * rather than inventing a failure on exotic version formats.\n */\nexport function unsupportedAiVersionMessage(version: string): string | undefined {\n const major = Number(/^v?(\\d+)(?:[.+-]|$)/.exec(version.trim())?.[1]);\n if (!Number.isSafeInteger(major) || major === SUPPORTED_AI_MAJOR) return undefined;\n return (\n `@coder/ai-sdk-agent supports the \\`ai\\` package v${SUPPORTED_AI_MAJOR} only ` +\n `(peer dependency \\`ai@^${SUPPORTED_AI_MAJOR}\\`), but ai@${version} is installed. ` +\n `Other majors fail at runtime in confusing ways (brand-check TypeErrors, silently ` +\n `empty streams). Install a compatible version, e.g. \\`pnpm add ai@^${SUPPORTED_AI_MAJOR}\\`.`\n );\n}\n\n/**\n * Best-effort resolution of the installed `ai` package's version via\n * `createRequire(import.meta.url)(\"ai/package.json\")` (both v6 and v7 export\n * `./package.json`). Returns `undefined` whenever resolution isn't possible —\n * non-Node runtime, bundle without module resolution, `ai` not installed, … —\n * because the guard must never introduce a failure of its own. `node:module`\n * is reached through `process.getBuiltinModule` (Node ≥ 20.16) instead of a\n * static import, which would crash non-Node runtimes at module-load time.\n */\nfunction resolveInstalledAiVersion(): string | undefined {\n try {\n const nodeModule =\n typeof process !== \"undefined\" && typeof process.getBuiltinModule === \"function\"\n ? process.getBuiltinModule(\"node:module\")\n : undefined;\n const requireFromHere = nodeModule?.createRequire(import.meta.url);\n const pkg = requireFromHere?.(\"ai/package.json\") as { version?: unknown } | undefined;\n return typeof pkg?.version === \"string\" ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Memoized verdict: `undefined` = not yet computed, `null` = supported (or\n * unresolvable), a string = the message to throw on every construction.\n */\nlet cachedMessage: string | null | undefined;\n\n/**\n * Asserts that the installed `ai` major matches this package's peer range\n * (`ai@^7`). Called from the `CoderAgent`/`CoderLanguageModel` constructors so\n * an incompatible AI SDK fails fast with an actionable error instead of the\n * cryptic failures it produces mid-generation. Fails open when the installed\n * version cannot be resolved. The verdict is memoized across calls.\n *\n * @param resolveVersion Test seam; production callers use the default.\n */\nexport function assertSupportedAiVersion(\n resolveVersion: () => string | undefined = resolveInstalledAiVersion,\n): void {\n if (cachedMessage === undefined) {\n const version = resolveVersion();\n cachedMessage = version === undefined ? null : (unsupportedAiVersionMessage(version) ?? null);\n }\n if (cachedMessage !== null) throw new CoderAgentError(cachedMessage);\n}\n\n/** Clears the memoized verdict so resolution runs again (unit-test hook). */\nexport function resetAiVersionCheckForTests(): void {\n cachedMessage = undefined;\n}\n","import type { SharedV4FileData } from \"@ai-sdk/provider\";\nimport { CoderAgentError } from \"./errors.js\";\n\n/**\n * Bytes to upload, in any convenient shape.\n *\n * `Blob`/`File` is the recommended form: it is lazy (not forced fully into\n * memory), carries its own size (so the 10 MiB chat cap can fail fast and a\n * `Content-Length` can be sent), is re-readable (so a failed upload can be\n * retried), and works both in Node (`fs.openAsBlob(\"./x.pdf\")`) and the browser\n * (an `<input type=file>` yields a `File`). A `ReadableStream` is accepted for\n * genuinely unbounded sources, but its size is unknown up front — the chat size\n * cap can then only be enforced server-side.\n */\nexport type FileContent = Blob | ReadableStream<Uint8Array> | Uint8Array | ArrayBuffer;\n\n/** A {@link FileContent} normalized for upload: a fetch-ready body plus derived metadata. */\nexport interface ResolvedFile {\n /**\n * Body to hand to `fetch`. Raw bytes are wrapped in a `Blob` so the body is\n * always a clean `BodyInit` (and bytes get a `Content-Length` for free).\n */\n body: Blob | ReadableStream<Uint8Array>;\n /** Resolved IANA media type. */\n mediaType: string;\n /** Original filename, if known. */\n name: string | undefined;\n /** Byte length, if known up front (undefined for streams). */\n size: number | undefined;\n}\n\nfunction isBlob(v: unknown): v is Blob {\n return typeof Blob !== \"undefined\" && v instanceof Blob;\n}\nfunction isReadableStream(v: unknown): v is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== \"undefined\" && v instanceof ReadableStream;\n}\n\n/**\n * Normalize {@link FileContent} into a fetch-ready body and derive its media\n * type, name, and (when known) size. `Blob`/`File` supply their own\n * `type`/`name`/`size`; for raw bytes or a stream, `mediaType` must be provided\n * by the caller. Explicit `opts.mediaType`/`opts.name` always win.\n */\nexport function resolveFileContent(\n content: FileContent,\n opts?: { mediaType?: string; name?: string },\n): ResolvedFile {\n let mediaType = opts?.mediaType;\n let name = opts?.name;\n let body: ResolvedFile[\"body\"];\n let size: number | undefined;\n\n if (isBlob(content)) {\n body = content;\n size = content.size;\n if (!mediaType && content.type) mediaType = content.type;\n // `File` extends `Blob` with a `name`; read it structurally so we don't\n // depend on the `File` global type existing.\n const maybeName = (content as { name?: unknown }).name;\n if (!name && typeof maybeName === \"string\") name = maybeName;\n } else if (isReadableStream(content)) {\n body = content;\n size = undefined;\n } else if (content instanceof Uint8Array || content instanceof ArrayBuffer) {\n size = content.byteLength;\n body = new Blob([content as BlobPart]);\n } else {\n throw new CoderAgentError(\n \"Unsupported file content: expected a Blob/File, ReadableStream, Uint8Array, or ArrayBuffer.\",\n );\n }\n\n if (!mediaType) {\n throw new CoderAgentError(\n \"Could not determine the file's media type. Pass `mediaType`, or provide a Blob/File that carries its own `type`.\",\n );\n }\n\n return { body, mediaType, name, size };\n}\n\n/** Decode a base64 string to bytes (Node `Buffer` or browser `atob`). */\nfunction base64ToBytes(b64: string): Uint8Array {\n // `Buffer` is already a `Uint8Array`; return it directly (no extra copy).\n if (typeof Buffer !== \"undefined\") return Buffer.from(b64, \"base64\");\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\n/**\n * Convert an AI SDK file-part `data` value (a tagged {@link SharedV4FileData}\n * union) into {@link FileContent} for upload. The provider receives inline\n * bytes, a base64 string, or inline text. URLs are not expected: this model\n * declares no `supportedUrls`, so the SDK downloads them to bytes before\n * calling us — a URL (or an opaque provider reference) here is therefore an\n * error.\n */\nexport function dataContentToFileContent(data: SharedV4FileData): FileContent {\n switch (data.type) {\n case \"data\":\n return data.data instanceof Uint8Array ? data.data : base64ToBytes(data.data);\n case \"text\":\n return new TextEncoder().encode(data.text);\n default:\n throw new CoderAgentError(\n `File parts referencing a ${data.type === \"url\" ? \"URL\" : \"provider reference\"} are not supported; provide the file bytes instead.`,\n );\n }\n}\n","/**\n * Wire types for Coder's experimental `chatd` chat API\n * (`/api/experimental/chats`). These mirror the Go types in\n * `coder/coder`'s `codersdk/chats.go` (JSON shapes), restricted to the subset\n * this client uses. Field names use snake_case to match the wire format.\n *\n * NOTE: The chatd API is experimental and may change between Coder releases.\n */\n\n// ---------------------------------------------------------------------------\n// Input (client -> server)\n// ---------------------------------------------------------------------------\n\nexport type ChatInputPartType = \"text\" | \"file\" | \"file-reference\";\n\nexport interface ChatInputPart {\n type: ChatInputPartType;\n text?: string;\n file_id?: string;\n file_name?: string;\n start_line?: number;\n end_line?: number;\n content?: string;\n}\n\n/**\n * Media types accepted by the chat-file upload endpoint\n * (`POST /api/experimental/chats/files`). The server allowlist is intentionally\n * narrow — anything else (zip, tarball, binaries, …) is not a chat attachment\n * and belongs on the workspace filesystem instead. Mirrors\n * `codersdk.AllChatAttachmentMediaTypes`.\n */\nexport type ChatAttachmentMediaType =\n | \"application/json\"\n | \"application/pdf\"\n | \"image/gif\"\n | \"image/jpeg\"\n | \"image/png\"\n | \"image/webp\"\n | \"text/csv\"\n | \"text/markdown\"\n | \"text/plain\";\n\n/** The chat-attachment media-type allowlist, as a runtime-checkable set. */\nexport const CHAT_ATTACHMENT_MEDIA_TYPES: ReadonlySet<string> = new Set<ChatAttachmentMediaType>([\n \"application/json\",\n \"application/pdf\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"text/csv\",\n \"text/markdown\",\n \"text/plain\",\n]);\n\n/** Per-file size cap enforced by the chat-file upload endpoint (10 MiB). */\nexport const MAX_CHAT_FILE_SIZE_BYTES = 10 * 1024 * 1024;\n\nexport type ChatClientType = \"ui\" | \"api\";\nexport type ChatPlanMode = \"\" | \"plan\";\nexport type ChatBusyBehavior = \"queue\" | \"interrupt\";\n\n/**\n * A client-executed (\"dynamic\") tool definition. chatd never executes these;\n * when the model calls one, the chat enters `requires_action` and the client\n * must execute it and submit results via {@link SubmitToolResultsRequest}.\n *\n * NOTE: the JSON key is `input_schema` (snake_case), per chatd.\n */\nexport interface DynamicTool {\n name: string;\n description?: string;\n /** A JSON Schema object describing the tool input. */\n input_schema: unknown;\n}\n\nexport interface CreateChatRequest {\n organization_id: string;\n content: ChatInputPart[];\n system_prompt?: string;\n workspace_id?: string;\n model_config_id?: string;\n mcp_server_ids?: string[];\n labels?: Record<string, string>;\n unsafe_dynamic_tools?: DynamicTool[];\n plan_mode?: ChatPlanMode;\n client_type?: ChatClientType;\n}\n\nexport interface CreateChatMessageRequest {\n content: ChatInputPart[];\n model_config_id?: string;\n mcp_server_ids?: string[];\n busy_behavior?: ChatBusyBehavior;\n plan_mode?: ChatPlanMode;\n}\n\n/** Result of a single client-executed tool call. */\nexport interface ToolResult {\n tool_call_id: string;\n /** JSON-serializable tool output. */\n output: unknown;\n is_error?: boolean;\n}\n\nexport interface SubmitToolResultsRequest {\n results: ToolResult[];\n}\n\nexport interface UpdateChatRequest {\n title?: string;\n archived?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Output (server -> client)\n// ---------------------------------------------------------------------------\n\nexport type ChatStatus =\n | \"waiting\"\n | \"pending\"\n | \"running\"\n | \"paused\"\n | \"completed\"\n | \"error\"\n | \"requires_action\"\n | \"interrupting\";\n\nexport type ChatMessageRole = \"system\" | \"user\" | \"assistant\" | \"tool\";\n\nexport type ChatMessagePartType =\n | \"text\"\n | \"reasoning\"\n | \"tool-call\"\n | \"tool-result\"\n | \"source\"\n | \"file\"\n | \"file-reference\"\n | \"context-file\"\n | \"skill\";\n\n/**\n * A part of a chat message. chatd models this as a single flat struct with a\n * `type` discriminator and many optional fields (see `codersdk.ChatMessagePart`),\n * so we mirror that shape rather than a strict discriminated union.\n */\nexport interface ChatMessagePart {\n type: ChatMessagePartType;\n\n // text / reasoning\n text?: string;\n signature?: string;\n\n // tool-call / tool-result\n tool_call_id?: string;\n tool_name?: string;\n mcp_server_config_id?: string | null;\n /** tool-call: complete arguments (JSON value). */\n args?: unknown;\n /** tool-call: incremental argument text chunk during streaming. */\n args_delta?: string;\n parsed_commands?: string[][];\n /** tool-result: complete result (JSON value). */\n result?: unknown;\n /** tool-result: incremental result text chunk during streaming. */\n result_delta?: string;\n result_reset?: boolean;\n is_error?: boolean;\n is_media?: boolean;\n provider_executed?: boolean;\n created_at?: string;\n completed_at?: string;\n\n // source\n source_id?: string;\n url?: string;\n title?: string;\n\n // file\n media_type?: string;\n name?: string;\n file_id?: string | null;\n\n // file-reference\n file_name?: string;\n start_line?: number;\n end_line?: number;\n content?: string;\n\n // skill\n skill_name?: string;\n skill_description?: string;\n}\n\nexport interface ChatMessageUsage {\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n reasoning_tokens?: number;\n cache_creation_tokens?: number;\n cache_read_tokens?: number;\n context_limit?: number;\n /**\n * Cost in micro-USD, when the server prices usage. Optional: servers that\n * don't price usage (or predate cost reporting) omit it.\n */\n total_cost_micros?: number;\n /** Model runtime in milliseconds, when the server reports it. */\n total_runtime_ms?: number;\n}\n\nexport interface ChatMessage {\n id: number;\n chat_id: string;\n created_by?: string;\n model_config_id?: string;\n created_at: string;\n role: ChatMessageRole;\n content?: ChatMessagePart[];\n usage?: ChatMessageUsage;\n}\n\nexport interface ChatErrorPayload {\n message: string;\n detail?: string;\n kind?: string;\n provider?: string;\n retryable?: boolean;\n status_code?: number;\n}\n\nexport interface Chat {\n id: string;\n organization_id: string;\n owner_id: string;\n owner_username?: string;\n workspace_id?: string | null;\n agent_id?: string | null;\n parent_chat_id?: string | null;\n root_chat_id?: string | null;\n last_model_config_id?: string;\n title: string;\n status: ChatStatus;\n plan_mode?: ChatPlanMode;\n last_error?: ChatErrorPayload | null;\n created_at: string;\n updated_at: string;\n archived: boolean;\n mcp_server_ids?: string[];\n client_type?: ChatClientType;\n}\n\nexport interface ChatQueuedMessage {\n id: number;\n chat_id: string;\n content: ChatMessagePart[];\n created_at: string;\n}\n\nexport interface CreateChatMessageResponse {\n message?: ChatMessage;\n queued_message?: ChatQueuedMessage;\n queued: boolean;\n warnings?: string[];\n}\n\nexport interface ChatMessagesResponse {\n messages: ChatMessage[];\n queued_messages: ChatQueuedMessage[];\n has_more: boolean;\n}\n\n/** Response from `POST /api/experimental/chats/files`: the uploaded file's id. */\nexport interface UploadChatFileResponse {\n id: string;\n}\n\n/**\n * A configured model (`GET /api/experimental/chats/model-configs`). Depending\n * on the server version and how a config was created, `provider`, `model`, and\n * `display_name` may be absent on individual entries, so treat them as\n * optional and guard reads.\n */\nexport interface ChatModelConfig {\n id: string;\n provider?: string;\n ai_provider_id?: string;\n model?: string;\n display_name?: string;\n enabled?: boolean;\n is_default?: boolean;\n context_limit?: number;\n compression_threshold?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Stream events (WebSocket `/stream`)\n// ---------------------------------------------------------------------------\n\nexport type ChatStreamEventType =\n | \"message_part\"\n | \"message\"\n | \"status\"\n | \"error\"\n | \"queue_update\"\n | \"retry\"\n | \"action_required\"\n | \"preview_reset\"\n | \"history_reset\";\n\nexport interface ChatStreamMessagePart {\n role?: ChatMessageRole;\n part: ChatMessagePart;\n history_version?: number;\n generation_attempt?: number;\n seq?: number;\n}\n\nexport interface ChatStreamStatus {\n status: ChatStatus;\n}\n\nexport interface ChatStreamToolCall {\n tool_call_id: string;\n tool_name: string;\n /** JSON string of the tool-call arguments. */\n args: string;\n}\n\nexport interface ChatStreamActionRequired {\n tool_calls: ChatStreamToolCall[];\n}\n\nexport interface ChatStreamRetry {\n attempt: number;\n delay_ms: number;\n error: string;\n kind?: string;\n provider?: string;\n status_code?: number;\n retrying_at: string;\n}\n\nexport interface ChatStreamEvent {\n type: ChatStreamEventType;\n chat_id: string;\n message?: ChatMessage;\n message_part?: ChatStreamMessagePart;\n status?: ChatStreamStatus;\n error?: ChatErrorPayload;\n retry?: ChatStreamRetry;\n queued_messages?: ChatQueuedMessage[];\n action_required?: ChatStreamActionRequired;\n}\n\n/** Terminal statuses: the chat has stopped generating for this turn. */\nexport const TERMINAL_STATUSES: ReadonlySet<ChatStatus> = new Set<ChatStatus>([\n \"waiting\",\n \"completed\",\n \"error\",\n \"requires_action\",\n]);\n\n// ---------------------------------------------------------------------------\n// Watch events (WebSocket `/watch`)\n// ---------------------------------------------------------------------------\n\n/** Kinds of chat lifecycle events emitted on the per-user watch socket. */\nexport type ChatWatchEventKind =\n | \"status_change\"\n | \"summary_change\"\n | \"title_change\"\n | \"created\"\n | \"deleted\"\n | \"diff_status_change\"\n | \"action_required\"\n | \"context_dirty\";\n\n/**\n * An event from `GET /api/experimental/chats/watch`, which covers every chat\n * visible to the authenticated user (not a single chat). Mirrors\n * `codersdk.ChatWatchEvent`.\n */\nexport interface ChatWatchEvent {\n kind: ChatWatchEventKind;\n chat: Chat;\n /** Pending client-executed tool calls; sent with `action_required` events. */\n tool_calls?: ChatStreamToolCall[];\n}\n","import NodeWebSocket from \"ws\";\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport type { ChatStreamEvent, ChatWatchEvent } from \"./types.js\";\n\n/**\n * A minimal WebSocket factory abstraction so the stream reader can run on\n * Node (via the `ws` package, with header auth) or in environments that\n * provide a global `WebSocket` (browsers, where auth must go in the query\n * string). Node is the default target.\n */\nexport interface WebSocketLike {\n send(data: string): void;\n close(code?: number): void;\n addEventListener(type: \"open\", cb: () => void): void;\n addEventListener(type: \"message\", cb: (ev: { data: unknown }) => void): void;\n addEventListener(type: \"error\", cb: (ev: unknown) => void): void;\n addEventListener(type: \"close\", cb: (ev: { code?: number; reason?: string }) => void): void;\n removeEventListener(type: \"open\", cb: () => void): void;\n removeEventListener(type: \"message\", cb: (ev: { data: unknown }) => void): void;\n removeEventListener(type: \"error\", cb: (ev: unknown) => void): void;\n removeEventListener(type: \"close\", cb: (ev: { code?: number; reason?: string }) => void): void;\n}\n\nexport type WebSocketFactory = (\n url: string,\n protocols: { headers: Record<string, string> },\n) => WebSocketLike;\n\nconst defaultFactory: WebSocketFactory = (url, { headers }) => {\n // `ws` accepts custom handshake headers, which lets us authenticate with the\n // `Coder-Session-Token` header instead of leaking the token into the URL.\n return new NodeWebSocket(url, { headers }) as unknown as WebSocketLike;\n};\n\nfunction httpToWs(baseUrl: string): string {\n if (baseUrl.startsWith(\"https://\")) return `wss://${baseUrl.slice(\"https://\".length)}`;\n if (baseUrl.startsWith(\"http://\")) return `ws://${baseUrl.slice(\"http://\".length)}`;\n return baseUrl;\n}\n\nexport interface StreamChatEventsOptions {\n baseUrl: string;\n token: string;\n chatId: string;\n /** Only stream events for messages with id greater than this. */\n afterId?: number;\n signal?: AbortSignal;\n webSocketFactory?: WebSocketFactory;\n}\n\n/**\n * Opens the chatd `/stream` WebSocket and yields decoded {@link ChatStreamEvent}s\n * as an async iterable. Each text frame from chatd is a JSON array of events;\n * we flatten them into a single stream.\n *\n * The chat stream is a live subscription that stays open after a turn settles,\n * so callers should `break` out of the loop once they observe a terminal status\n * — that triggers generator cleanup, which closes the socket.\n */\nexport async function* streamChatEvents(\n options: StreamChatEventsOptions,\n): AsyncGenerator<ChatStreamEvent, void, void> {\n const { baseUrl, token, chatId, afterId, signal } = options;\n const factory = options.webSocketFactory ?? defaultFactory;\n\n const wsBase = httpToWs(baseUrl);\n const query = afterId !== undefined ? `?after_id=${afterId}` : \"\";\n const url = `${wsBase}/api/experimental/chats/${chatId}/stream${query}`;\n\n const queue: ChatStreamEvent[] = [];\n let resolveNext: (() => void) | undefined;\n let finished = false;\n let failure: Error | undefined;\n\n const wake = () => {\n resolveNext?.();\n resolveNext = undefined;\n };\n\n const ws = factory(url, { headers: { \"Coder-Session-Token\": token } });\n\n const onAbort = () => {\n finished = true;\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n wake();\n };\n let abortListenerAdded = false;\n if (signal) {\n if (signal.aborted) onAbort();\n else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n abortListenerAdded = true;\n }\n }\n\n const onMessage = (ev: { data: unknown }): void => {\n if (finished) return;\n let batch: unknown;\n try {\n const data = typeof ev.data === \"string\" ? ev.data : String(ev.data);\n batch = JSON.parse(data);\n } catch (err) {\n failure = new CoderAgentError(\"failed to parse chat stream frame\", { cause: err });\n finished = true;\n wake();\n return;\n }\n if (Array.isArray(batch)) {\n for (const e of batch) queue.push(e as ChatStreamEvent);\n } else if (batch && typeof batch === \"object\") {\n queue.push(batch as ChatStreamEvent);\n }\n wake();\n };\n const onError = (ev: unknown): void => {\n if (finished) return;\n const message =\n ev && typeof ev === \"object\" && \"message\" in ev\n ? String((ev as { message: unknown }).message)\n : \"chat stream socket error\";\n failure = new CoderAgentError(message);\n finished = true;\n wake();\n };\n const onClose = (): void => {\n finished = true;\n wake();\n };\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n try {\n while (true) {\n while (queue.length > 0) {\n const next = queue.shift() as ChatStreamEvent;\n yield next;\n }\n if (failure) throw failure;\n if (finished) return;\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n }\n } finally {\n finished = true;\n if (signal && abortListenerAdded) signal.removeEventListener(\"abort\", onAbort);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n }\n}\n\nconst WATCH_PATH = \"/api/experimental/chats/watch\";\nconst WATCH_BACKOFF_INITIAL_MS = 1_000;\nconst WATCH_BACKOFF_CAP_MS = 30_000;\n\n/** Abort-aware sleep; resolves early (without throwing) when the signal aborts. */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n // The timer stays ref'd on purpose: an actively consumed watch loop must\n // keep the process alive through a reconnect delay (abort clears it).\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Extract the HTTP status from a rejected WebSocket upgrade. The `ws` package\n * reports handshake failures as an error event whose message is\n * `Unexpected server response: <status>`; other factories that follow the\n * same convention are recognized too.\n */\nfunction upgradeStatus(message: string): number | undefined {\n const match = /unexpected server response: (\\d{3})$/i.exec(message.trim());\n return match ? Number(match[1]) : undefined;\n}\n\nexport interface WatchChatEventsOptions {\n baseUrl: string;\n token: string;\n signal?: AbortSignal;\n webSocketFactory?: WebSocketFactory;\n}\n\n/**\n * Opens the chatd `/watch` WebSocket — lifecycle events for every chat visible\n * to the authenticated user — and yields decoded {@link ChatWatchEvent}s as an\n * async iterable.\n *\n * Unlike the per-chat `/stream`, this is a long-lived subscription, so dropped\n * connections are redialed automatically with exponential backoff (1s doubling\n * to a 30s cap, reset once an event arrives). It ends in exactly three ways:\n * `signal` aborting, the consumer ending iteration (`break` from `for await`,\n * `iterator.return()`) — both tear the socket down promptly even while a read\n * is pending — or the server rejecting the upgrade with a 4xx — bad/expired\n * token, or an older Coder server without the endpoint (404) — which throws a\n * terminal {@link CoderApiError} instead of retrying forever.\n */\nexport function watchChatEvents(\n options: WatchChatEventsOptions,\n): AsyncGenerator<ChatWatchEvent, void, void> {\n // Cancellation plumbing: async-generator `return()`/`throw()` queue behind a\n // pending `next()`, and on this rarely-eventing stream a pending read is the\n // steady state — a bare `return()` would otherwise hang (socket open) until\n // the next server event. The wrapper aborts an internal controller first,\n // which wakes the pending read, then delegates.\n const controller = new AbortController();\n const external = options.signal;\n const chain = (): void => controller.abort(external?.reason);\n if (external) {\n if (external.aborted) chain();\n else external.addEventListener(\"abort\", chain, { once: true });\n }\n const inner = watchChatEventsLoop({ ...options, signal: controller.signal });\n const detach = (): void => external?.removeEventListener(\"abort\", chain);\n const finish = async (): Promise<void> => {\n controller.abort();\n detach();\n try {\n await inner.return();\n } catch {\n /* the loop's own teardown errors are not the caller's problem */\n }\n };\n return {\n // Detach the signal chain once the loop settles for good (done, or the\n // terminal 4xx rejection) — the iteration protocol never calls return()\n // on a failed iterator, and the {once} listener would otherwise pile up\n // on a long-lived signal across re-created watchers.\n async next(): Promise<IteratorResult<ChatWatchEvent, void>> {\n try {\n const result = await inner.next();\n if (result.done) detach();\n return result;\n } catch (err) {\n detach();\n throw err;\n }\n },\n async return(): Promise<IteratorResult<ChatWatchEvent, void>> {\n await finish();\n return { done: true, value: undefined };\n },\n async throw(err: unknown): Promise<IteratorResult<ChatWatchEvent, void>> {\n await finish();\n throw err;\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n // Native async generators are async-disposable; the wrapper must be too,\n // so `await using events = client.watchChats(…)` keeps tearing down.\n async [Symbol.asyncDispose](): Promise<void> {\n await finish();\n },\n } as AsyncGenerator<ChatWatchEvent, void, void>;\n}\n\nasync function* watchChatEventsLoop(\n options: WatchChatEventsOptions,\n): AsyncGenerator<ChatWatchEvent, void, void> {\n const { baseUrl, token, signal } = options;\n const factory = options.webSocketFactory ?? defaultFactory;\n const url = `${httpToWs(baseUrl)}${WATCH_PATH}`;\n\n let backoffMs = WATCH_BACKOFF_INITIAL_MS;\n while (!signal?.aborted) {\n // One connection attempt per iteration; the queue/wake plumbing mirrors\n // streamChatEvents above.\n const queue: ChatWatchEvent[] = [];\n let resolveNext: (() => void) | undefined;\n let finished = false;\n let failure: Error | undefined;\n\n const wake = () => {\n resolveNext?.();\n resolveNext = undefined;\n };\n\n const ws = factory(url, { headers: { \"Coder-Session-Token\": token } });\n\n const onAbort = () => {\n finished = true;\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n wake();\n };\n let abortListenerAdded = false;\n if (signal) {\n if (signal.aborted) onAbort();\n else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n abortListenerAdded = true;\n }\n }\n\n const onMessage = (ev: { data: unknown }): void => {\n if (finished) return;\n let payload: unknown;\n try {\n const data = typeof ev.data === \"string\" ? ev.data : String(ev.data);\n payload = JSON.parse(data);\n } catch (err) {\n // A malformed frame means this connection is unusable; redial it.\n failure = new CoderAgentError(\"failed to parse chat watch frame\", { cause: err });\n finished = true;\n wake();\n return;\n }\n // chatd sends one event object per frame (wsjson); tolerate batches too.\n if (Array.isArray(payload)) {\n for (const e of payload) queue.push(e as ChatWatchEvent);\n } else if (payload && typeof payload === \"object\") {\n queue.push(payload as ChatWatchEvent);\n }\n wake();\n };\n const onError = (ev: unknown): void => {\n if (finished) return;\n const message =\n ev && typeof ev === \"object\" && \"message\" in ev\n ? String((ev as { message: unknown }).message)\n : \"chat watch socket error\";\n const status = upgradeStatus(message);\n failure =\n status !== undefined && status >= 400 && status < 500\n ? new CoderApiError({ status, method: \"GET\", path: WATCH_PATH, message })\n : new CoderAgentError(message);\n finished = true;\n wake();\n };\n const onClose = (): void => {\n finished = true;\n wake();\n };\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n try {\n while (true) {\n while (queue.length > 0) {\n const next = queue.shift() as ChatWatchEvent;\n // Receiving an event proves the connection works — reset the backoff.\n backoffMs = WATCH_BACKOFF_INITIAL_MS;\n yield next;\n }\n if (finished) break;\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n }\n } finally {\n finished = true;\n if (signal && abortListenerAdded) signal.removeEventListener(\"abort\", onAbort);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close(1000);\n } catch {\n /* ignore */\n }\n }\n\n if (signal?.aborted) return;\n // A rejected upgrade (4xx) is terminal: retrying with the same credentials\n // against the same server cannot succeed.\n if (failure instanceof CoderApiError) throw failure;\n await sleep(backoffMs, signal);\n if (signal?.aborted) return;\n backoffMs = Math.min(backoffMs * 2, WATCH_BACKOFF_CAP_MS);\n }\n}\n","import { CoderAgentError, CoderApiError } from \"../errors.js\";\nimport { type FileContent, resolveFileContent } from \"../files.js\";\nimport {\n type Chat,\n CHAT_ATTACHMENT_MEDIA_TYPES,\n type ChatMessagesResponse,\n type ChatModelConfig,\n type ChatStreamEvent,\n type ChatWatchEvent,\n type CreateChatMessageRequest,\n type CreateChatMessageResponse,\n type CreateChatRequest,\n MAX_CHAT_FILE_SIZE_BYTES,\n type SubmitToolResultsRequest,\n type UpdateChatRequest,\n type UploadChatFileResponse,\n} from \"./types.js\";\nimport { streamChatEvents, watchChatEvents, type WebSocketFactory } from \"./ws.js\";\n\n/** A file to upload as a chat attachment. */\nexport interface ChatFileInput {\n content: FileContent;\n /**\n * Media type. Required unless `content` is a Blob/File with a non-empty `type`\n * — note `fs.openAsBlob()` returns a Blob with no type, so pass it explicitly there.\n */\n mediaType?: string;\n /** Original filename, surfaced to the model and UI. Defaults to a File's `name`. */\n name?: string;\n}\n\n/** A file that has been uploaded to chat-file storage. */\nexport interface UploadedChatFile {\n /** Server-assigned file id, referenced from message content via a `file` part. */\n id: string;\n mediaType: string;\n name?: string;\n}\n\nexport interface CoderChatClientOptions {\n /** Base URL of the Coder deployment, e.g. `https://dev.coder.com`. */\n baseUrl: string;\n /** Coder API token or session token (sent as `Coder-Session-Token`). */\n token: string;\n /** Custom fetch implementation (defaults to global `fetch`). */\n fetch?: typeof globalThis.fetch;\n /** Custom WebSocket factory (defaults to the `ws` package on Node). */\n webSocketFactory?: WebSocketFactory;\n}\n\nconst API_PREFIX = \"/api/experimental/chats\";\n\n/**\n * A thin, typed client for Coder's experimental `chatd` API. This is a\n * TypeScript port of the chat surface of `codersdk.ExperimentalClient`.\n */\nexport class CoderChatClient {\n readonly baseUrl: string;\n readonly #token: string;\n readonly #fetch: typeof globalThis.fetch;\n readonly #webSocketFactory: WebSocketFactory | undefined;\n\n constructor(options: CoderChatClientOptions) {\n // Normalize: strip a single trailing slash.\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.#token = options.token;\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.#webSocketFactory = options.webSocketFactory;\n }\n\n /**\n * Issue a request and return the raw `Response` on success (body unread). On a\n * non-2xx status the body is consumed to build a {@link CoderApiError}. Shared\n * by JSON requests and the raw upload/download endpoints.\n */\n async #send(\n method: string,\n path: string,\n opts?: { body?: BodyInit; headers?: Record<string, string>; signal?: AbortSignal },\n ): Promise<Response> {\n const headers: Record<string, string> = {\n \"Coder-Session-Token\": this.#token,\n ...opts?.headers,\n };\n const init: RequestInit = { method, headers, body: opts?.body, signal: opts?.signal };\n // A streaming request body requires half-duplex mode on Node's fetch (undici).\n if (typeof ReadableStream !== \"undefined\" && opts?.body instanceof ReadableStream) {\n (init as RequestInit & { duplex?: \"half\" }).duplex = \"half\";\n }\n\n const res = await this.#fetch(`${this.baseUrl}${path}`, init);\n if (!res.ok) {\n const errObj = (await this.#json<{ message?: string; detail?: string }>(res)) ?? {};\n throw new CoderApiError({\n status: res.status,\n method,\n path,\n message: errObj.message ?? res.statusText ?? \"request failed\",\n detail: errObj.detail,\n });\n }\n return res;\n }\n\n /** Read a response body as JSON, tolerating an unreadable/empty/malformed body (→ undefined). */\n async #json<T>(res: Response): Promise<T | undefined> {\n const text = await res.text().catch(() => \"\");\n if (text.length === 0) return undefined;\n try {\n return JSON.parse(text) as T;\n } catch {\n return undefined;\n }\n }\n\n async #request<T>(\n method: string,\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const res = await this.#send(method, path, {\n body: body === undefined ? undefined : JSON.stringify(body),\n headers: body === undefined ? undefined : { \"Content-Type\": \"application/json\" },\n signal,\n });\n return (await this.#json<T>(res)) as T;\n }\n\n // --- REST -----------------------------------------------------------------\n\n listModelConfigs(signal?: AbortSignal): Promise<ChatModelConfig[]> {\n return this.#request<ChatModelConfig[]>(\n \"GET\",\n `${API_PREFIX}/model-configs`,\n undefined,\n signal,\n );\n }\n\n createChat(req: CreateChatRequest, signal?: AbortSignal): Promise<Chat> {\n return this.#request<Chat>(\"POST\", API_PREFIX, req, signal);\n }\n\n getChat(chatId: string, signal?: AbortSignal): Promise<Chat> {\n return this.#request<Chat>(\"GET\", `${API_PREFIX}/${chatId}`, undefined, signal);\n }\n\n createChatMessage(\n chatId: string,\n req: CreateChatMessageRequest,\n signal?: AbortSignal,\n ): Promise<CreateChatMessageResponse> {\n return this.#request<CreateChatMessageResponse>(\n \"POST\",\n `${API_PREFIX}/${chatId}/messages`,\n req,\n signal,\n );\n }\n\n getMessages(\n chatId: string,\n opts?: { before_id?: number; after_id?: number; limit?: number },\n signal?: AbortSignal,\n ): Promise<ChatMessagesResponse> {\n const params = new URLSearchParams();\n if (opts?.before_id !== undefined) params.set(\"before_id\", String(opts.before_id));\n if (opts?.after_id !== undefined) params.set(\"after_id\", String(opts.after_id));\n if (opts?.limit !== undefined) params.set(\"limit\", String(opts.limit));\n const q = params.toString();\n return this.#request<ChatMessagesResponse>(\n \"GET\",\n `${API_PREFIX}/${chatId}/messages${q ? `?${q}` : \"\"}`,\n undefined,\n signal,\n );\n }\n\n submitToolResults(\n chatId: string,\n req: SubmitToolResultsRequest,\n signal?: AbortSignal,\n ): Promise<void> {\n return this.#request<void>(\"POST\", `${API_PREFIX}/${chatId}/tool-results`, req, signal);\n }\n\n /**\n * Interrupt the chat's in-flight run (`POST /interrupt`). The server flips\n * an active run to `interrupting` and responds immediately with the updated\n * chat — it does not wait for the run to actually stop.\n *\n * Pass `{ wait: true }` to send `?wait=true`, asking the server to hold the\n * response until the run has stopped. Coder servers without that param\n * ignore the unknown query harmlessly and still return immediately, so\n * callers should be prepared to confirm completion via the event stream.\n *\n * The second parameter also accepts a bare `AbortSignal` (the historical\n * signature).\n */\n interruptChat(\n chatId: string,\n optsOrSignal?: AbortSignal | { wait?: boolean; signal?: AbortSignal },\n ): Promise<Chat> {\n const opts = optsOrSignal instanceof AbortSignal ? { signal: optsOrSignal } : optsOrSignal;\n const query = opts?.wait ? \"?wait=true\" : \"\";\n return this.#request<Chat>(\n \"POST\",\n `${API_PREFIX}/${chatId}/interrupt${query}`,\n undefined,\n opts?.signal,\n );\n }\n\n updateChat(chatId: string, req: UpdateChatRequest, signal?: AbortSignal): Promise<void> {\n return this.#request<void>(\"PATCH\", `${API_PREFIX}/${chatId}`, req, signal);\n }\n\n /** Convenience: archive a chat (soft-hide; safe for cleanup). */\n archiveChat(chatId: string, signal?: AbortSignal): Promise<void> {\n return this.updateChat(chatId, { archived: true }, signal);\n }\n\n // --- Files ----------------------------------------------------------------\n\n /**\n * Upload a file as a durable chat attachment, returning its id (referenced\n * from message content via a `file` input part). The server enforces a narrow\n * media-type allowlist and a 10 MiB cap; both are checked client-side first so\n * unsupported files fail fast with a clear error instead of an opaque 4xx.\n *\n * The body is sent raw (not multipart): `Content-Type` carries the media type\n * and `Content-Disposition` the filename, mirroring `codersdk`.\n */\n async uploadChatFile(\n organizationId: string,\n file: ChatFileInput,\n signal?: AbortSignal,\n ): Promise<UploadedChatFile> {\n const resolved = resolveFileContent(file.content, {\n mediaType: file.mediaType,\n name: file.name,\n });\n // Match the allowlist on the media type alone, ignoring parameters such as\n // `; charset=utf-8` that a Blob/File commonly carries.\n const mediaType = (resolved.mediaType.split(\";\")[0] ?? resolved.mediaType).trim().toLowerCase();\n if (!CHAT_ATTACHMENT_MEDIA_TYPES.has(mediaType)) {\n throw new CoderAgentError(\n `Media type \"${mediaType}\" is not allowed for chat attachments ` +\n `(allowed: ${[...CHAT_ATTACHMENT_MEDIA_TYPES].join(\", \")}). ` +\n `Write other file types to a workspace instead.`,\n );\n }\n if (resolved.size !== undefined && resolved.size > MAX_CHAT_FILE_SIZE_BYTES) {\n throw new CoderAgentError(\n `File is ${resolved.size} bytes, over the ${MAX_CHAT_FILE_SIZE_BYTES}-byte ` +\n `(10 MiB) chat attachment limit. Write large files to a workspace instead.`,\n );\n }\n\n const headers: Record<string, string> = { \"Content-Type\": mediaType };\n if (resolved.name) {\n // RFC 6266: a sanitized ASCII `filename` plus an RFC 5987 `filename*` that\n // preserves the exact name. Both are pure ASCII, so a non-Latin-1 name\n // (CJK, emoji, accents) can't trip fetch's ByteString header check, and\n // CR/LF/quote/backslash are encoded away rather than breaking the header.\n const ascii = resolved.name.replace(/[^\\x20-\\x7e]/g, \"_\").replace(/([\"\\\\])/g, \"\\\\$1\");\n const utf8 = encodeURIComponent(resolved.name).replace(\n /['()*]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n headers[\"Content-Disposition\"] = `attachment; filename=\"${ascii}\"; filename*=UTF-8''${utf8}`;\n }\n const res = await this.#send(\n \"POST\",\n `${API_PREFIX}/files?organization=${encodeURIComponent(organizationId)}`,\n { body: resolved.body, headers, signal },\n );\n const parsed = await this.#json<UploadChatFileResponse>(res);\n if (!parsed?.id) {\n throw new CoderApiError({\n status: res.status,\n method: \"POST\",\n path: `${API_PREFIX}/files`,\n message: \"upload succeeded but the response contained no file id\",\n });\n }\n return { id: parsed.id, mediaType, name: resolved.name };\n }\n\n /** Download a chat file's bytes and media type by id. */\n async getChatFile(\n fileId: string,\n signal?: AbortSignal,\n ): Promise<{ bytes: Uint8Array; mediaType: string }> {\n const res = await this.#send(\"GET\", `${API_PREFIX}/files/${fileId}`, { signal });\n const buf = await res.arrayBuffer();\n return {\n bytes: new Uint8Array(buf),\n mediaType: res.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n };\n }\n\n // --- Streaming ------------------------------------------------------------\n\n streamEvents(\n chatId: string,\n opts?: { afterId?: number; signal?: AbortSignal },\n ): AsyncGenerator<ChatStreamEvent, void, void> {\n return streamChatEvents({\n baseUrl: this.baseUrl,\n token: this.#token,\n chatId,\n afterId: opts?.afterId,\n signal: opts?.signal,\n webSocketFactory: this.#webSocketFactory,\n });\n }\n\n /**\n * Watch lifecycle events (status/title changes, creation, deletion, …) for\n * every chat visible to the authenticated user via the `/chats/watch`\n * WebSocket. Dropped connections are redialed with exponential backoff; the\n * iteration ends only when `opts.signal` aborts, or with a terminal\n * {@link CoderApiError} when the server rejects the upgrade with a 4xx\n * (bad/expired token, or an older Coder server without the endpoint → 404).\n */\n watchChats(opts?: { signal?: AbortSignal }): AsyncGenerator<ChatWatchEvent, void, void> {\n return watchChatEvents({\n baseUrl: this.baseUrl,\n token: this.#token,\n signal: opts?.signal,\n webSocketFactory: this.#webSocketFactory,\n });\n }\n\n // --- Helpers --------------------------------------------------------------\n\n /**\n * Resolves a user-friendly model hint to a model-config UUID.\n *\n * Accepts: a config UUID (returned as-is), a `provider:model` id\n * (e.g. `anthropic:claude-haiku-4-5-20251001`), a bare model id, or a\n * display-name substring (case-insensitive). Returns `undefined` if no\n * match is found, in which case the caller should let chatd pick the default.\n *\n * Tolerates partial payloads from older/newer servers: a malformed listing\n * (empty body, non-array JSON, null entries) resolves as no match, and\n * entries missing `provider`, `model`, or `display_name` are matched on the\n * fields they do carry — an entry without `provider` still matches by its\n * model id, one without `model` only by display name.\n */\n async resolveModelConfigId(hint: string, signal?: AbortSignal): Promise<string | undefined> {\n const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (UUID_RE.test(hint)) return hint;\n\n const raw: unknown = await this.listModelConfigs(signal);\n const configs = (Array.isArray(raw) ? raw : []).filter(\n (c): c is ChatModelConfig => typeof c === \"object\" && c !== null,\n );\n const lower = hint.toLowerCase();\n // `provider:model` form.\n const colon = hint.indexOf(\":\");\n const provider = colon >= 0 ? hint.slice(0, colon).toLowerCase() : undefined;\n const model = colon >= 0 ? hint.slice(colon + 1).toLowerCase() : lower;\n\n const candidates = configs.filter((c) => c.enabled !== false);\n const pool = candidates.length > 0 ? candidates : configs;\n\n // Guarded accessors: treat a missing/non-string field as absent.\n const modelOf = (c: ChatModelConfig) =>\n typeof c.model === \"string\" ? c.model.toLowerCase() : undefined;\n const providerOf = (c: ChatModelConfig) =>\n typeof c.provider === \"string\" ? c.provider.toLowerCase() : undefined;\n const displayNameOf = (c: ChatModelConfig) =>\n typeof c.display_name === \"string\" ? c.display_name.toLowerCase() : undefined;\n\n const exact = pool.find(\n (c) => modelOf(c) === model && (provider === undefined || providerOf(c) === provider),\n );\n if (exact) return exact.id;\n\n const byModel = pool.find((c) => modelOf(c) === model);\n if (byModel) return byModel.id;\n\n const byDisplay = pool.find((c) => displayNameOf(c)?.includes(lower));\n if (byDisplay) return byDisplay.id;\n\n const byModelSubstring = pool.find((c) => modelOf(c)?.includes(model));\n return byModelSubstring?.id;\n }\n}\n","/**\n * Minimal helpers for the stable Coder v2 REST endpoints that the workspace\n * preview feature composes: workspace lookup, the wildcard app host, and port\n * sharing. Deliberately tiny — only the fields this package reads are modeled\n * — and mirroring `CoderChatClient`'s conventions (`Coder-Session-Token`\n * auth, `CoderApiError` on non-2xx). These are internal building blocks for\n * `CoderAgent.getPreview`/`sharePreview`; only their option/result types are\n * part of the public API.\n */\n\nimport { CoderAgentError, CoderApiError } from \"../errors.js\";\n\n/** Connection details for the stable v2 API (same credentials as the chat client). */\nexport interface WorkspaceApiConnection {\n /** Base URL of the Coder deployment, e.g. `https://dev.coder.com`. */\n baseUrl: string;\n /** Coder API/session token (sent as `Coder-Session-Token`). */\n token: string;\n /** Custom fetch implementation (defaults to global `fetch`). */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * Subset of `codersdk.Workspace` (GET `/api/v2/workspaces/{workspace}`) that\n * the preview helpers read. Resources without agents omit the `agents` key\n * entirely, so the whole chain is treated as optional.\n */\nexport interface WorkspaceSummary {\n id: string;\n /** Username of the workspace owner (a subdomain URL segment). */\n owner_name: string;\n name: string;\n latest_build?: {\n resources?: { agents?: { name: string }[] }[];\n };\n}\n\n/** Response of GET `/api/v2/applications/host`; `host` is `\"\"` when subdomain apps are disabled. */\nexport interface AppHostResponse {\n /** Wildcard app host with a literal `*`, e.g. `*.apps.example.com`, possibly with a port. */\n host: string;\n}\n\n/** Share levels a port can be in; `owner` is the implicit default when no share exists. */\nexport type PortShareLevel = \"owner\" | \"authenticated\" | \"organization\" | \"public\";\n\n/**\n * Share levels the upsert endpoint accepts. `owner` is rejected server-side —\n * reverting to owner-only is done by deleting the share, not setting it.\n */\nexport type PreviewShareLevel = Exclude<PortShareLevel, \"owner\">;\n\n/** Protocol the workspace app speaks on the port (drives the `s` suffix in the URL). */\nexport type PortShareProtocol = \"http\" | \"https\";\n\n/** Body of POST `/api/v2/workspaces/{workspace}/port-share` (a true upsert on agent+port). */\nexport interface UpsertPortShareRequest {\n agent_name: string;\n port: number;\n share_level: PreviewShareLevel;\n protocol: PortShareProtocol;\n}\n\n/** A port-share row, mirroring `codersdk.WorkspaceAgentPortShare`. */\nexport interface WorkspaceAgentPortShare {\n workspace_id: string;\n agent_name: string;\n port: number;\n share_level: PortShareLevel;\n protocol: PortShareProtocol;\n}\n\n/**\n * Issue a JSON request against the v2 API. Mirrors `CoderChatClient`: the\n * session-token header, and a {@link CoderApiError} carrying the server's\n * `message`/`detail` on any non-2xx (body tolerated missing or malformed).\n */\nasync function request<T>(\n conn: WorkspaceApiConnection,\n method: string,\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n): Promise<T> {\n const fetchFn = conn.fetch ?? globalThis.fetch.bind(globalThis);\n const headers: Record<string, string> = { \"Coder-Session-Token\": conn.token };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n\n const res = await fetchFn(`${conn.baseUrl.replace(/\\/$/, \"\")}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n const text = await res.text().catch(() => \"\");\n let parsed: unknown;\n try {\n parsed = text.length > 0 ? JSON.parse(text) : undefined;\n } catch {\n parsed = undefined;\n }\n if (!res.ok) {\n const errObj = (parsed ?? {}) as { message?: string; detail?: string };\n throw new CoderApiError({\n status: res.status,\n method,\n path,\n message: errObj.message ?? res.statusText ?? \"request failed\",\n detail: errObj.detail,\n });\n }\n return parsed as T;\n}\n\n/** Fetch a workspace (owner, name, agents) by UUID: GET `/api/v2/workspaces/{workspace}`. */\nexport function getWorkspace(\n conn: WorkspaceApiConnection,\n workspaceId: string,\n signal?: AbortSignal,\n): Promise<WorkspaceSummary> {\n return request<WorkspaceSummary>(\n conn,\n \"GET\",\n `/api/v2/workspaces/${workspaceId}`,\n undefined,\n signal,\n );\n}\n\n/**\n * Fetch the deployment's wildcard app host: GET `/api/v2/applications/host`.\n * The endpoint predates port sharing and exists on all supported servers;\n * an empty `host` means no wildcard access URL is configured.\n */\nexport function getAppsHost(\n conn: WorkspaceApiConnection,\n signal?: AbortSignal,\n): Promise<AppHostResponse> {\n return request<AppHostResponse>(conn, \"GET\", \"/api/v2/applications/host\", undefined, signal);\n}\n\n/**\n * Create or update a port share: POST `/api/v2/workspaces/{workspace}/port-share`\n * (an upsert keyed on agent+port; re-posting updates level/protocol in place).\n * A 404 is rethrown with a hint that the server may predate port sharing —\n * the whole route is absent on Coder servers older than ~v2.9.\n */\nexport async function upsertPortShare(\n conn: WorkspaceApiConnection,\n workspaceId: string,\n req: UpsertPortShareRequest,\n signal?: AbortSignal,\n): Promise<WorkspaceAgentPortShare> {\n const path = `/api/v2/workspaces/${workspaceId}/port-share`;\n try {\n return await request<WorkspaceAgentPortShare>(conn, \"POST\", path, req, signal);\n } catch (err) {\n if (err instanceof CoderApiError && err.status === 404) {\n throw new CoderApiError({\n status: 404,\n method: \"POST\",\n path,\n message: \"port sharing is unavailable\",\n detail:\n \"the workspace was not found, or this Coder server predates port sharing (added in Coder v2.9)\",\n });\n }\n throw err;\n }\n}\n\n/** Inputs for building a preview URL (see `CoderAgent.getPreview`). */\nexport interface ResolvePreviewOptions {\n workspaceId: string;\n /** Workspace port the app listens on. */\n port: number;\n /** Agent that serves the port; defaults to the workspace's only agent. */\n agentName?: string;\n /** Protocol the app speaks inside the workspace (not the browser scheme). Default `\"http\"`. */\n protocol?: PortShareProtocol;\n}\n\n/** The workspace's only agent's name, or a clear error naming the candidates. */\nfunction defaultAgentName(workspace: WorkspaceSummary): string {\n const agents = (workspace.latest_build?.resources ?? []).flatMap((r) => r.agents ?? []);\n const first = agents[0];\n if (agents.length === 1 && first) return first.name;\n if (agents.length === 0) {\n throw new CoderAgentError(\n `Workspace \"${workspace.name}\" has no agents in its latest build (is it running?); ` +\n `pass agentName explicitly.`,\n );\n }\n throw new CoderAgentError(\n `Workspace \"${workspace.name}\" has ${agents.length} agents ` +\n `(${agents.map((a) => a.name).join(\", \")}); pass agentName to pick one.`,\n );\n}\n\n/**\n * Ports must be integral, in TCP range, and — stricter than TCP — at least\n * 1000: Coder's subdomain app parser (`appurl.PortRegex`, `^\\d{4,5}s?$`) only\n * treats 4–5 digit labels as ports, so a URL for port 80 would be parsed as an\n * app named \"80\" and never resolve. Reject those up front instead of returning\n * a dead URL.\n */\nfunction assertValidPort(port: number): void {\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new CoderAgentError(`Invalid port ${port}: expected an integer between 1 and 65535.`);\n }\n if (port < 1000) {\n throw new CoderAgentError(\n `Port ${port} cannot be previewed: Coder subdomain app URLs only encode 4-5 digit ports ` +\n `(1000-65535). Serve the preview on a higher port instead.`,\n );\n }\n}\n\n/**\n * Compose the subdomain preview URL for a workspace port the same way the\n * dashboard does: the label `{port}{s?}--{agent}--{workspace}--{owner}`\n * replaces the `*` in the deployment's wildcard app host, and the browser\n * scheme follows the deployment's own access URL. The trailing `s` (from\n * `protocol: \"https\"`) tells the proxy to speak TLS *to the workspace port*;\n * it does not affect the browser scheme.\n */\nexport async function resolveWorkspacePreview(\n conn: WorkspaceApiConnection,\n opts: ResolvePreviewOptions,\n signal?: AbortSignal,\n): Promise<{ url: string; agentName: string }> {\n assertValidPort(opts.port);\n const [workspace, appHost] = await Promise.all([\n getWorkspace(conn, opts.workspaceId, signal),\n getAppsHost(conn, signal),\n ]);\n if (!appHost.host) {\n throw new CoderAgentError(\n \"This Coder deployment has no wildcard app host configured (--wildcard-access-url), \" +\n \"so subdomain preview URLs are unavailable.\",\n );\n }\n const agentName = opts.agentName ?? defaultAgentName(workspace);\n const suffix = opts.protocol === \"https\" ? \"s\" : \"\";\n const label = `${opts.port}${suffix}--${agentName}--${workspace.name}--${workspace.owner_name}`;\n // The host keeps a literal `*` (possibly inside the first label, e.g.\n // `*--apps.example.com`) and may carry an explicit port on dev deployments —\n // substitute the label into the whole host string, dashboard-style.\n const scheme = new URL(conn.baseUrl).protocol;\n return { url: `${scheme}//${appHost.host.replace(/\\*/g, label)}`, agentName };\n}\n\n/** Inputs for sharing and previewing a workspace port (see `CoderAgent.sharePreview`). */\nexport interface SharePreviewRequest extends ResolvePreviewOptions {\n /** Who may open the port. Default `\"authenticated\"`. */\n shareLevel?: PreviewShareLevel;\n}\n\n/**\n * Resolve a port's preview URL, then upsert its share level so the URL is\n * reachable beyond the workspace owner. Returns the server-confirmed level\n * (falling back to the requested one when the server omits it).\n */\nexport async function shareWorkspacePreview(\n conn: WorkspaceApiConnection,\n opts: SharePreviewRequest,\n signal?: AbortSignal,\n): Promise<{ url: string; shareLevel: PreviewShareLevel }> {\n const requested = opts.shareLevel ?? \"authenticated\";\n const { url, agentName } = await resolveWorkspacePreview(conn, opts, signal);\n const share = await upsertPortShare(\n conn,\n opts.workspaceId,\n {\n agent_name: agentName,\n port: opts.port,\n share_level: requested,\n protocol: opts.protocol ?? \"http\",\n },\n signal,\n );\n // Old servers may answer with an empty/partial body; trust it when present.\n const level = share?.share_level && share.share_level !== \"owner\" ? share.share_level : requested;\n return { url, shareLevel: level };\n}\n","import type {\n LanguageModelV4CallOptions,\n LanguageModelV4Message,\n LanguageModelV4Prompt,\n LanguageModelV4ToolResultOutput,\n SharedV4FileData,\n} from \"@ai-sdk/provider\";\nimport type { ChatInputPart, DynamicTool, ToolResult } from \"../coder/types.js\";\n\n/** Namespace key for this package's options on AI SDK message-part `providerOptions`. */\nexport const CODER_PROVIDER_OPTIONS = \"coder\";\n\n/** Shape of this package's `providerOptions.coder` payload on a file part. */\nexport interface CoderFileProviderOptions {\n /** A pre-uploaded chat-file id to reuse instead of uploading the part's bytes. */\n fileId?: string;\n}\n\n/** Concatenated system messages, mapped to chatd's `system_prompt`. */\nexport function extractSystemPrompt(prompt: LanguageModelV4Prompt): string | undefined {\n const parts = prompt.filter(\n (m): m is Extract<LanguageModelV4Message, { role: \"system\" }> => m.role === \"system\",\n );\n if (parts.length === 0) return undefined;\n const joined = parts\n .map((m) => m.content)\n .join(\"\\n\\n\")\n .trim();\n return joined.length > 0 ? joined : undefined;\n}\n\n/** The content array of a user message (text and file parts). */\nexport type UserContent = Extract<LanguageModelV4Message, { role: \"user\" }>[\"content\"];\n\n/**\n * Uploads a file part's bytes and resolves to its chatd file id. Supplied by\n * the language model (which holds the client + organization id); kept abstract\n * here so {@link userContentToInputParts} stays pure and testable.\n */\nexport type FilePartUploader = (file: {\n data: SharedV4FileData;\n mediaType: string;\n filename?: string;\n}) => Promise<string>;\n\n/** Reads a pre-uploaded file id from a file part's `providerOptions.coder.fileId`. */\nfunction coderFileId(providerOptions: unknown): string | undefined {\n const ns = (providerOptions as Record<string, CoderFileProviderOptions> | undefined)?.[\n CODER_PROVIDER_OPTIONS\n ];\n // Require a non-empty id: an empty string is not a usable reference, so fall\n // back to uploading rather than emitting `file_id: \"\"`.\n return typeof ns?.fileId === \"string\" && ns.fileId.length > 0 ? ns.fileId : undefined;\n}\n\n/**\n * Maps a user message's content to chatd input parts. Text passes through;\n * file parts are turned into `file` parts referencing an uploaded file id —\n * either reused from `providerOptions.coder.fileId` (a pre-uploaded handle) or\n * uploaded on the fly via {@link FilePartUploader}.\n */\nexport async function userContentToInputParts(\n content: UserContent,\n uploadFile: FilePartUploader,\n): Promise<ChatInputPart[]> {\n // Uploads are independent, so run them concurrently; `map` preserves order.\n const parts = await Promise.all(\n content.map(async (part): Promise<ChatInputPart | null> => {\n if (part.type === \"text\") {\n return part.text.length > 0 ? { type: \"text\", text: part.text } : null;\n }\n if (part.type === \"file\") {\n const fileId =\n coderFileId(part.providerOptions) ??\n (await uploadFile({\n data: part.data,\n mediaType: part.mediaType,\n filename: part.filename,\n }));\n return { type: \"file\", file_id: fileId };\n }\n return null;\n }),\n );\n return parts.filter((p): p is ChatInputPart => p !== null);\n}\n\nfunction toolResultOutputToChatd(output: LanguageModelV4ToolResultOutput): {\n value: unknown;\n isError: boolean;\n} {\n switch (output.type) {\n case \"text\":\n return { value: output.value, isError: false };\n case \"json\":\n return { value: output.value, isError: false };\n case \"error-text\":\n return { value: output.value, isError: true };\n case \"error-json\":\n return { value: output.value, isError: true };\n case \"execution-denied\":\n return { value: { execution_denied: true, reason: output.reason }, isError: true };\n case \"content\":\n return { value: output.value, isError: false };\n default:\n return { value: output, isError: false };\n }\n}\n\nexport type TurnAction =\n | { kind: \"new-turn\"; content: UserContent }\n | { kind: \"resume\"; toolResults: ToolResult[] }\n | { kind: \"noop\" };\n\n/**\n * Decides what to do with a fresh `doStream`/`doGenerate` prompt:\n * - trailing `tool` message(s) → resume the in-flight chatd turn by submitting\n * those tool results (the AI SDK executed a client tool between steps);\n * - otherwise the trailing `user` message → a new turn.\n */\nexport function classifyTurnAction(prompt: LanguageModelV4Prompt): TurnAction {\n if (prompt.length === 0) return { kind: \"noop\" };\n const last = prompt[prompt.length - 1];\n if (!last) return { kind: \"noop\" };\n\n if (last.role === \"tool\") {\n const toolResults: ToolResult[] = [];\n // Collect from all trailing consecutive tool messages.\n for (let i = prompt.length - 1; i >= 0; i--) {\n const m = prompt[i];\n if (!m || m.role !== \"tool\") break;\n for (const part of m.content) {\n if (part.type !== \"tool-result\") continue;\n const { value, isError } = toolResultOutputToChatd(part.output);\n toolResults.unshift({ tool_call_id: part.toolCallId, output: value, is_error: isError });\n }\n }\n return { kind: \"resume\", toolResults };\n }\n\n if (last.role === \"user\") {\n return { kind: \"new-turn\", content: last.content };\n }\n\n return { kind: \"noop\" };\n}\n\n/** Maps AI SDK function tools to chatd client-executed (\"dynamic\") tools. */\nexport function toolsToDynamicTools(tools: LanguageModelV4CallOptions[\"tools\"]): DynamicTool[] {\n if (!tools) return [];\n const out: DynamicTool[] = [];\n for (const tool of tools) {\n if (tool.type !== \"function\") continue; // provider tools are not forwarded\n out.push({\n name: tool.name,\n description: tool.description,\n input_schema: tool.inputSchema,\n });\n }\n return out;\n}\n\n/** The set of tool names the AI SDK side owns (i.e. client-executed). */\nexport function dynamicToolNames(tools: LanguageModelV4CallOptions[\"tools\"]): Set<string> {\n const names = new Set<string>();\n for (const tool of tools ?? []) {\n if (tool.type === \"function\") names.add(tool.name);\n }\n return names;\n}\n","import type {\n JSONObject,\n LanguageModelV4FinishReason,\n LanguageModelV4StreamPart,\n LanguageModelV4Usage,\n} from \"@ai-sdk/provider\";\nimport { CoderChatError } from \"../errors.js\";\nimport {\n type ChatErrorPayload,\n type ChatMessage,\n type ChatMessagePart,\n type ChatMessageUsage,\n type ChatStatus,\n type ChatStreamEvent,\n TERMINAL_STATUSES,\n} from \"../coder/types.js\";\n\n/** `a + b` where an undefined operand contributes nothing; undefined only when both are. */\nfunction addDefined(a: number | undefined, b: number | undefined): number | undefined {\n if (b === undefined) return a;\n return (a ?? 0) + b;\n}\n\n/**\n * How each known wire usage field aggregates across a segment's steps:\n * counters sum; `context_limit` is a property of the model rather than a\n * counter, so the newest step's value wins. Typed against\n * {@link ChatMessageUsage} so adding a wire field without classifying it\n * fails the build instead of silently vanishing from the totals. Fields this\n * SDK doesn't know yet (newer servers) pass through with newest-wins\n * semantics so `usage.raw` stays the escape hatch for them.\n */\nconst USAGE_FIELD_KINDS = {\n input_tokens: \"sum\",\n output_tokens: \"sum\",\n total_tokens: \"sum\",\n reasoning_tokens: \"sum\",\n cache_creation_tokens: \"sum\",\n cache_read_tokens: \"sum\",\n total_cost_micros: \"sum\",\n total_runtime_ms: \"sum\",\n context_limit: \"newest\",\n} as const satisfies Record<keyof ChatMessageUsage, \"sum\" | \"newest\">;\n\nfunction mapUsage(u: ChatMessageUsage | undefined): LanguageModelV4Usage {\n // chatd normalizes provider usage so `input_tokens` counts only the UNCACHED\n // prompt tokens — cache reads/writes are reported separately (both Anthropic\n // and OpenAI are normalized this way server-side). The V4 `inputTokens.total`\n // is the full prompt size, so the cache components must be added back; with\n // prompt caching active, `input_tokens` alone is near zero.\n return {\n inputTokens: {\n total: addDefined(\n addDefined(u?.input_tokens, u?.cache_read_tokens),\n u?.cache_creation_tokens,\n ),\n noCache: u?.input_tokens,\n cacheRead: u?.cache_read_tokens,\n cacheWrite: u?.cache_creation_tokens,\n },\n outputTokens: {\n total: u?.output_tokens,\n text: undefined,\n reasoning: u?.reasoning_tokens,\n },\n // Preserve the turn's wire usage (snake_case) so callers can reach fields\n // the normalized shape has no slot for (context_limit, cost, runtime, …).\n ...(u ? { raw: u as unknown as JSONObject } : {}),\n };\n}\n\n/**\n * Sums per-message wire usage into segment totals. chatd attaches usage to\n * every assistant message it commits — one per internal model step — so the\n * segment's consumption is the sum over all of them, not the last message's\n * (the AI SDK then sums segments into the turn total). Fields stay absent\n * unless at least one message reported them (old servers keep looking\n * unchanged); non-counter and unknown fields take the newest reporting\n * message's value (see {@link USAGE_FIELD_KINDS}).\n */\nfunction accumulateUsage(\n byMessageId: ReadonlyMap<number, ChatMessageUsage>,\n): ChatMessageUsage | undefined {\n if (byMessageId.size === 0) return undefined;\n // Wire usage is parsed JSON; newer servers may send fields beyond\n // ChatMessageUsage, so accumulate on the JSON shape and narrow at the end.\n const total: JSONObject = {};\n // Map insertion order is not id order after a revision re-set, so track the\n // reporting message id per newest-wins key explicitly.\n const newestIdByKey = new Map<string, number>();\n for (const [id, u] of byMessageId) {\n for (const [key, value] of Object.entries(u)) {\n if (value === undefined) continue;\n if (USAGE_FIELD_KINDS[key as keyof ChatMessageUsage] === \"sum\" && typeof value === \"number\") {\n total[key] = ((total[key] as number | undefined) ?? 0) + value;\n } else if (id > (newestIdByKey.get(key) ?? -1)) {\n // `context_limit`, malformed counters, and any field this SDK doesn't\n // know yet: not summable — the newest reporting message's value wins,\n // which keeps `usage.raw` a forward-compatible escape hatch.\n newestIdByKey.set(key, id);\n total[key] = value;\n }\n }\n }\n return total as ChatMessageUsage;\n}\n\nfunction jsonResult(value: unknown): NonNullable<unknown> {\n // chatd tool results are arbitrary JSON; the V4 tool-result `result` must be\n // a non-null JSON value.\n return (value ?? {}) as NonNullable<unknown>;\n}\n\n/**\n * Translates one chatd turn's {@link ChatStreamEvent} stream into a sequence of\n * `LanguageModelV4StreamPart`s for the AI SDK.\n *\n * Two text/reasoning modes, decided per assistant message from the wire\n * behavior:\n * - **delta mode** — chatd streamed `message_part` deltas; we emit those and\n * treat the message's trailing full `message` snapshot as a no-op for\n * text/reasoning. Deltas carry no message id, so \"deltas arrived since the\n * last assistant snapshot\" is what marks a snapshot as trailing.\n * - **snapshot mode** — fast turns where only full `message` snapshots arrive;\n * we diff each snapshot's full text against what we've already emitted.\n * Both paths track an emitted-length cursor, so neither double-counts.\n *\n * Client (custom) tool calls are emitted from the reliable `action_required`\n * event and left for the AI SDK to execute. chatd's own server-side tools are\n * surfaced best-effort as `providerExecuted` tool calls/results.\n */\nexport class TurnTranslator {\n readonly #dynamicToolNames: ReadonlySet<string>;\n\n #seq = 0;\n #text = { id: undefined as string | undefined, len: 0, sawDelta: false };\n #reasoning = { id: undefined as string | undefined, len: 0, sawDelta: false };\n #currentAssistantId: number | undefined;\n // Whether text/reasoning deltas arrived since the last assistant `message`\n // snapshot — i.e. the next assistant snapshot is the trailing snapshot of the\n // message those deltas streamed (deltas carry no message id of their own).\n #deltasSinceSnapshot = false;\n\n #serverToolCalls = new Set<string>();\n #serverToolResults = new Set<string>();\n #clientToolCalls = new Set<string>();\n #clientToolCallSeen = false;\n #sources = new Set<string>();\n\n // Per-message wire usage, keyed by message id so a re-streamed revision of\n // the same message replaces its earlier entry instead of double-counting\n // (chatd re-sends full snapshots on revision bumps and history resets).\n readonly #usageByMessageId = new Map<number, ChatMessageUsage>();\n #error: ChatErrorPayload | undefined;\n #terminalStatus: ChatStatus | undefined;\n #maxMessageId = 0;\n\n /**\n * Messages with an id at or below this cursor belong to earlier turns (or\n * to a segment that already streamed) — they arrive only as replays: the\n * initial sync when resuming a chat, or a mid-turn `history_reset` re-send\n * of the full history. Ingest skips them entirely so a replay can neither\n * re-emit earlier turns' content nor inflate this turn's usage.\n */\n readonly #turnCursor: number;\n\n constructor(opts: { dynamicToolNames: ReadonlySet<string>; turnCursor?: number }) {\n this.#dynamicToolNames = opts.dynamicToolNames;\n this.#turnCursor = opts.turnCursor ?? 0;\n }\n\n get terminalStatus(): ChatStatus | undefined {\n return this.#terminalStatus;\n }\n /** Whether a client (custom) tool call has been emitted this turn. */\n get clientToolCallSeen(): boolean {\n return this.#clientToolCallSeen;\n }\n get maxMessageId(): number {\n return this.#maxMessageId;\n }\n get error(): ChatErrorPayload | undefined {\n return this.#error;\n }\n\n // --- block helpers --------------------------------------------------------\n\n #openText(out: LanguageModelV4StreamPart[]): void {\n if (this.#reasoning.id) this.#closeReasoning(out);\n if (!this.#text.id) {\n this.#text.id = `text-${++this.#seq}`;\n this.#text.len = 0;\n out.push({ type: \"text-start\", id: this.#text.id });\n }\n }\n #closeText(out: LanguageModelV4StreamPart[]): void {\n if (this.#text.id) {\n out.push({ type: \"text-end\", id: this.#text.id });\n this.#text.id = undefined;\n this.#text.len = 0;\n }\n }\n #openReasoning(out: LanguageModelV4StreamPart[]): void {\n if (this.#text.id) this.#closeText(out);\n if (!this.#reasoning.id) {\n this.#reasoning.id = `reasoning-${++this.#seq}`;\n this.#reasoning.len = 0;\n out.push({ type: \"reasoning-start\", id: this.#reasoning.id });\n }\n }\n #closeReasoning(out: LanguageModelV4StreamPart[]): void {\n if (this.#reasoning.id) {\n out.push({ type: \"reasoning-end\", id: this.#reasoning.id });\n this.#reasoning.id = undefined;\n this.#reasoning.len = 0;\n }\n }\n\n #emitTextUpTo(out: LanguageModelV4StreamPart[], full: string): void {\n if (full.length <= this.#text.len && this.#text.id) return;\n this.#openText(out);\n if (full.length > this.#text.len) {\n out.push({\n type: \"text-delta\",\n id: this.#text.id as string,\n delta: full.slice(this.#text.len),\n });\n this.#text.len = full.length;\n }\n }\n #emitReasoningUpTo(out: LanguageModelV4StreamPart[], full: string): void {\n if (full.length <= this.#reasoning.len && this.#reasoning.id) return;\n this.#openReasoning(out);\n if (full.length > this.#reasoning.len) {\n out.push({\n type: \"reasoning-delta\",\n id: this.#reasoning.id as string,\n delta: full.slice(this.#reasoning.len),\n });\n this.#reasoning.len = full.length;\n }\n }\n\n // --- tool helpers ---------------------------------------------------------\n\n #isClientTool(name: string | undefined): boolean {\n return name !== undefined && this.#dynamicToolNames.has(name);\n }\n\n #emitServerToolCall(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const id = part.tool_call_id;\n const name = part.tool_name;\n if (!id || !name) return;\n if (part.args === undefined) return; // wait for complete args (snapshot)\n if (this.#serverToolCalls.has(id)) return;\n this.#serverToolCalls.add(id);\n // `dynamic: true` is load-bearing: server tools are not in the client ToolSet, and\n // the AI SDK only tolerates unknown tool names on `providerExecuted && dynamic`\n // calls. Without it every server tool call is marked `invalid`, which injects a\n // phantom tool-error output and halts the tool loop on that step — stranding the\n // turn whenever a client tool call is pending in the same segment.\n out.push({\n type: \"tool-input-start\",\n id,\n toolName: name,\n providerExecuted: true,\n dynamic: true,\n });\n out.push({ type: \"tool-input-end\", id });\n out.push({\n type: \"tool-call\",\n toolCallId: id,\n toolName: name,\n input: typeof part.args === \"string\" ? part.args : JSON.stringify(part.args),\n providerExecuted: true,\n dynamic: true,\n });\n }\n\n #emitServerToolResult(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const id = part.tool_call_id;\n const name = part.tool_name;\n if (!id || !name) return;\n if (part.result === undefined) return;\n if (this.#serverToolResults.has(id)) return;\n // Only pair with a call emitted in THIS segment. A result whose call streamed in\n // an earlier segment (chatd paused for a client tool in between, and the resume\n // segment starts past the assistant message) would reach the AI SDK call-less,\n // and generateText throws \"Tool call <id> not found.\" Drop the orphan — the\n // server-side transcript still has it.\n if (!this.#serverToolCalls.has(id)) return;\n this.#serverToolResults.add(id);\n out.push({\n type: \"tool-result\",\n toolCallId: id,\n toolName: name,\n result: jsonResult(part.result),\n isError: part.is_error ?? false,\n // Mirror the call's `dynamic: true` so call and result land in the same\n // bucket (steps[*].dynamicToolCalls / dynamicToolResults, UI streams).\n dynamic: true,\n });\n }\n\n /**\n * Emits a chatd `source` part as a standalone V4 url source (no text-block\n * bracketing needed). Deduped by the emitted id so a part streamed as a\n * `message_part` isn't re-emitted by its trailing `message` snapshot, while\n * snapshot-only turns still emit theirs.\n */\n #emitSource(out: LanguageModelV4StreamPart[], part: ChatMessagePart): void {\n const url = part.url;\n if (!url) return; // the V4 source part requires both id and url\n const id = part.source_id || url;\n if (this.#sources.has(id)) return;\n this.#sources.add(id);\n out.push({\n type: \"source\",\n sourceType: \"url\",\n id,\n url,\n ...(part.title !== undefined ? { title: part.title } : {}),\n });\n }\n\n // --- ingest ---------------------------------------------------------------\n\n ingest(ev: ChatStreamEvent): LanguageModelV4StreamPart[] {\n const out: LanguageModelV4StreamPart[] = [];\n switch (ev.type) {\n case \"message_part\":\n this.#ingestMessagePart(out, ev);\n break;\n case \"message\":\n if (ev.message) this.#ingestMessage(out, ev.message);\n break;\n case \"action_required\":\n for (const tc of ev.action_required?.tool_calls ?? []) {\n if (this.#clientToolCalls.has(tc.tool_call_id)) continue;\n this.#closeText(out);\n this.#closeReasoning(out);\n this.#clientToolCalls.add(tc.tool_call_id);\n this.#clientToolCallSeen = true;\n out.push({ type: \"tool-input-start\", id: tc.tool_call_id, toolName: tc.tool_name });\n out.push({ type: \"tool-input-end\", id: tc.tool_call_id });\n out.push({\n type: \"tool-call\",\n toolCallId: tc.tool_call_id,\n toolName: tc.tool_name,\n input: tc.args,\n });\n }\n break;\n case \"error\":\n if (ev.error) {\n this.#error = ev.error;\n out.push({ type: \"error\", error: new CoderChatError(ev.error) });\n }\n break;\n case \"status\":\n if (ev.status && TERMINAL_STATUSES.has(ev.status.status)) {\n this.#terminalStatus = ev.status.status;\n }\n break;\n default:\n break; // retry / queue_update / preview_reset / history_reset\n }\n return out;\n }\n\n #ingestMessagePart(out: LanguageModelV4StreamPart[], ev: ChatStreamEvent): void {\n const mp = ev.message_part;\n if (!mp || (mp.role !== \"assistant\" && mp.role !== \"tool\")) return;\n const part = mp.part;\n switch (part.type) {\n case \"text\":\n this.#text.sawDelta = true;\n this.#deltasSinceSnapshot = true;\n this.#openText(out);\n if (part.text) {\n out.push({ type: \"text-delta\", id: this.#text.id as string, delta: part.text });\n this.#text.len += part.text.length;\n }\n break;\n case \"reasoning\":\n this.#reasoning.sawDelta = true;\n this.#deltasSinceSnapshot = true;\n this.#openReasoning(out);\n if (part.text) {\n out.push({ type: \"reasoning-delta\", id: this.#reasoning.id as string, delta: part.text });\n this.#reasoning.len += part.text.length;\n }\n break;\n case \"tool-call\":\n this.#closeText(out);\n this.#closeReasoning(out);\n if (!this.#isClientTool(part.tool_name)) this.#emitServerToolCall(out, part);\n break;\n case \"tool-result\":\n if (!this.#isClientTool(part.tool_name)) this.#emitServerToolResult(out, part);\n break;\n case \"source\":\n this.#emitSource(out, part);\n break;\n default:\n break;\n }\n }\n\n #ingestMessage(out: LanguageModelV4StreamPart[], message: ChatMessage): void {\n if (message.id > this.#maxMessageId) this.#maxMessageId = message.id;\n // Replay of an earlier turn (or an already-streamed segment): skip it\n // entirely — see #turnCursor. In the normal path the server-side\n // `after_id` filter means such ids never arrive, so this only bites on\n // replays, where processing them would re-emit old content as this\n // turn's output and double-count its usage.\n if (message.id <= this.#turnCursor) return;\n // chatd attaches usage to the assistant messages it commits (one per\n // internal model step); tool messages never carry usage, and under\n // summing, accepting a hypothetical mirrored copy would double-count.\n if (message.usage && message.role === \"assistant\") {\n this.#usageByMessageId.set(message.id, message.usage);\n }\n const content = message.content ?? [];\n\n if (message.role === \"assistant\") {\n // New assistant message boundary: close prior blocks and reset cursors.\n // Skipped when deltas arrived since the previous assistant snapshot:\n // deltas carry no message id, so they belong to the message THIS snapshot\n // finalizes (its id differing from the previous snapshot's), and the\n // snapshot must stay a no-op for text/reasoning. The reset is load-bearing\n // for snapshot-only messages, which must still diff-and-emit below. Known\n // tradeoff: a mid-message snapshot followed by more deltas of the SAME\n // message would misclassify the next snapshot — that ordering is outside\n // the trailing-snapshot protocol (see class doc).\n if (\n this.#currentAssistantId !== undefined &&\n this.#currentAssistantId !== message.id &&\n !this.#deltasSinceSnapshot\n ) {\n this.#closeText(out);\n this.#closeReasoning(out);\n this.#text.sawDelta = false;\n this.#reasoning.sawDelta = false;\n }\n this.#currentAssistantId = message.id;\n\n if (!this.#text.sawDelta) {\n const full = content\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n if (full.length > 0) this.#emitTextUpTo(out, full);\n }\n if (!this.#reasoning.sawDelta) {\n const full = content\n .filter((p) => p.type === \"reasoning\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n if (full.length > 0) this.#emitReasoningUpTo(out, full);\n }\n // Tool calls/results and sources are id-deduped, so snapshots process them\n // unconditionally (even when the snapshot is a text/reasoning no-op).\n for (const part of content) {\n if (part.type === \"tool-call\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolCall(out, part);\n else if (part.type === \"tool-result\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolResult(out, part);\n else if (part.type === \"source\") this.#emitSource(out, part);\n }\n this.#deltasSinceSnapshot = false;\n } else if (message.role === \"tool\") {\n for (const part of content) {\n if (part.type === \"tool-result\" && !this.#isClientTool(part.tool_name))\n this.#emitServerToolResult(out, part);\n else if (part.type === \"source\") this.#emitSource(out, part);\n }\n }\n }\n\n // --- finish ---------------------------------------------------------------\n\n finish(): LanguageModelV4StreamPart[] {\n const out: LanguageModelV4StreamPart[] = [];\n this.#closeText(out);\n this.#closeReasoning(out);\n\n let unified: LanguageModelV4FinishReason[\"unified\"];\n if (this.#error || this.#terminalStatus === \"error\") unified = \"error\";\n else if (this.#clientToolCallSeen || this.#terminalStatus === \"requires_action\")\n unified = \"tool-calls\";\n else unified = \"stop\";\n\n const usage = accumulateUsage(this.#usageByMessageId);\n\n // Surface server-side cost/runtime (sent by newer servers as extra usage\n // fields), summed over the segment's steps, under `providerMetadata.coder`.\n // Omitted entirely when the server sent neither, so old servers look\n // unchanged. NOTE: the AI SDK propagates only the FINAL segment's\n // providerMetadata to `result.providerMetadata` — whole-turn cost for a\n // client-tool turn is the sum over `result.steps[*].providerMetadata`.\n const coder: JSONObject = {};\n if (usage?.total_cost_micros !== undefined) coder.total_cost_micros = usage.total_cost_micros;\n if (usage?.total_runtime_ms !== undefined) coder.total_runtime_ms = usage.total_runtime_ms;\n\n out.push({\n type: \"finish\",\n usage: mapUsage(usage),\n finishReason: { unified, raw: this.#terminalStatus },\n ...(Object.keys(coder).length > 0 ? { providerMetadata: { coder } } : {}),\n });\n return out;\n }\n}\n","import type {\n LanguageModelV4,\n LanguageModelV4CallOptions,\n LanguageModelV4Content,\n LanguageModelV4GenerateResult,\n LanguageModelV4StreamPart,\n LanguageModelV4StreamResult,\n LanguageModelV4Usage,\n SharedV4Warning,\n} from \"@ai-sdk/provider\";\nimport { assertSupportedAiVersion } from \"../ai-version.js\";\nimport { CoderAgentError, CoderApiError, CoderChatError } from \"../errors.js\";\nimport { CoderChatClient } from \"../coder/client.js\";\nimport type { ChatInputPart, CreateChatRequest } from \"../coder/types.js\";\nimport { dataContentToFileContent } from \"../files.js\";\nimport {\n classifyTurnAction,\n dynamicToolNames,\n extractSystemPrompt,\n type FilePartUploader,\n toolsToDynamicTools,\n type UserContent,\n userContentToInputParts,\n} from \"./prompt.js\";\nimport { TurnTranslator } from \"./translate.js\";\n\nconst EMPTY_USAGE: LanguageModelV4Usage = {\n inputTokens: {\n total: undefined,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: { total: undefined, text: undefined, reasoning: undefined },\n};\n\nexport interface CoderLanguageModelConfig {\n client: CoderChatClient;\n organizationId: string;\n /** Model hint (UUID, `provider:model`, model id, or display-name substring). */\n model?: string;\n /** Bind the chat to a Coder workspace (enables workspace tools). */\n workspaceId?: string;\n /** chatd-side MCP servers to enable. */\n mcpServerIds?: string[];\n planMode?: \"\" | \"plan\";\n /** Resume an existing chat instead of creating a new one. */\n chatId?: string;\n /**\n * Per-segment time budget in milliseconds, applied to each model round-trip\n * (one `doStream`/`doGenerate` call: chat creation or message/tool-result\n * submission, plus the server-side run until it settles or pauses for a client\n * tool). If exceeded, the run is interrupted server-side and the call rejects\n * with a retryable {@link CoderChatError} (`kind: \"timeout\"`). A multi-step\n * `generate()` that drives client tools makes several segments, so this bounds\n * each segment, not the whole call — to cap total wall-clock, pass\n * `abortSignal: AbortSignal.timeout(ms)`. Unset or non-positive means no limit.\n */\n requestTimeoutMs?: number;\n}\n\n/**\n * A {@link LanguageModelV4} that is backed by a remote Coder `chatd` agent\n * runtime instead of a raw LLM. One model instance owns one chatd chat\n * (a \"session\"): the chat is created lazily on the first turn and reused for\n * subsequent turns and for client-tool resume steps.\n *\n * The chatd server runs the agent loop (model calls, server-side tools,\n * compaction) itself. This model therefore represents *one chatd segment* per\n * `doStream` call — it advances the chat until it settles (`waiting`/\n * `completed`) or pauses for a client tool (`requires_action`). When the AI SDK\n * executes a client tool and calls `doStream` again with the tool result, this\n * model resumes the same chat. The two loops mesh at the client-tool boundary.\n *\n * NOTE: a single model instance is single-flight — do not run concurrent\n * generations against the same instance/session.\n */\nexport class CoderLanguageModel implements LanguageModelV4 {\n readonly specificationVersion = \"v4\" as const;\n readonly provider = \"coder.chatd\";\n readonly modelId: string;\n readonly supportedUrls: Record<string, RegExp[]> = {};\n\n readonly #config: CoderLanguageModelConfig;\n #chatId: string | undefined;\n #lastSeenMessageId = 0;\n #resolvedModelConfigId: string | undefined;\n #modelResolved = false;\n readonly #submittedToolCallIds = new Set<string>();\n #inFlight = false;\n\n constructor(config: CoderLanguageModelConfig) {\n // Fail fast on an incompatible AI SDK major (see peer dependency `ai@^7`).\n assertSupportedAiVersion();\n this.#config = config;\n this.modelId = config.model ?? \"chatd\";\n this.#chatId = config.chatId;\n }\n\n get chatId(): string | undefined {\n return this.#chatId;\n }\n\n /** Drops the current session so the next turn creates a fresh chat. */\n resetSession(): void {\n this.#chatId = undefined;\n this.#lastSeenMessageId = 0;\n this.#submittedToolCallIds.clear();\n }\n\n async #resolveModelConfigId(signal?: AbortSignal): Promise<string | undefined> {\n if (this.#modelResolved) return this.#resolvedModelConfigId;\n if (this.#config.model) {\n this.#resolvedModelConfigId = await this.#config.client.resolveModelConfigId(\n this.#config.model,\n signal,\n );\n }\n this.#modelResolved = true;\n return this.#resolvedModelConfigId;\n }\n\n /**\n * Resolve a user message's content to chatd input parts, uploading any file\n * parts to chat-file storage (the upload endpoint needs only the organization\n * id, so this runs before the chat exists). Pre-uploaded files carried via\n * `providerOptions.coder.fileId` are referenced without re-uploading.\n */\n #buildContent(content: UserContent, signal?: AbortSignal): Promise<ChatInputPart[]> {\n const uploadFile: FilePartUploader = async (f) => {\n const uploaded = await this.#config.client.uploadChatFile(\n this.#config.organizationId,\n { content: dataContentToFileContent(f.data), mediaType: f.mediaType, name: f.filename },\n signal,\n );\n return uploaded.id;\n };\n return userContentToInputParts(content, uploadFile);\n }\n\n async *#runTurn(\n options: LanguageModelV4CallOptions,\n ): AsyncGenerator<LanguageModelV4StreamPart, void, void> {\n // A single model instance owns one chatd session's mutable state, so it is\n // single-flight: reject overlapping turns rather than silently corrupting\n // chatId / lastSeenMessageId / submitted tool-call tracking.\n if (this.#inFlight) {\n throw new CoderAgentError(\n \"A generation is already in flight on this CoderAgent (single-flight). Use a separate CoderAgent instance for concurrent sessions.\",\n );\n }\n this.#inFlight = true;\n\n // Combine the caller's abort signal with an optional per-turn timeout into a\n // single signal (the platform composes and cleans these up for us). When\n // there's neither a caller signal nor a timeout, `signal` stays undefined and\n // there is no per-turn setup. Keep a reference to our *own* timeout signal so\n // a timeout stays distinguishable from a caller abort — even when the caller's\n // own signal is itself an `AbortSignal.timeout` (whose reason is a TimeoutError\n // too, so reason-sniffing alone would misclassify it).\n const externalSignal = options.abortSignal;\n const timeoutMs = this.#config.requestTimeoutMs;\n const timeoutSignal =\n timeoutMs !== undefined && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;\n const sources = [externalSignal, timeoutSignal].filter(\n (s): s is AbortSignal => s !== undefined,\n );\n const signal: AbortSignal | undefined =\n sources.length > 0 ? AbortSignal.any(sources) : undefined;\n\n // Declared here so `finally` can read whether the turn settled; constructed\n // only once the turn's starting message id is known (see `turnCursor`).\n let translator: TurnTranslator | undefined;\n\n // Interrupting the *server* run (not just closing the WebSocket) is what frees\n // the chat's workspace/resources. Fire it at most once — on abort/timeout, and\n // on teardown of an unsettled turn (see `finally`), which also covers stream\n // `cancel()` and premature close, neither of which aborts the signal. A chat\n // whose id we never received (createChat aborted mid-flight) can't be reached.\n let interruptSent = false;\n const interrupt = (): void => {\n const id = this.#chatId;\n if (interruptSent || !id) return;\n interruptSent = true;\n void this.#config.client.interruptChat(id).catch(() => {});\n };\n signal?.addEventListener(\"abort\", interrupt, { once: true });\n\n // Map an abort of the combined signal to the right error. Our own\n // `timeoutSignal` having fired means a per-turn timeout; otherwise it's a\n // caller abort, re-thrown as the caller's reason (preserving AbortError so the\n // AI SDK still recognizes the cancellation).\n const throwIfAborted = (): void => {\n if (timeoutSignal?.aborted) {\n throw new CoderChatError({\n message: `Coder Agent turn exceeded its ${timeoutMs}ms requestTimeoutMs budget.`,\n kind: \"timeout\",\n retryable: true,\n });\n }\n if (signal?.aborted) {\n throw (\n externalSignal?.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\")\n );\n }\n };\n\n try {\n const { prompt } = options;\n\n // chatd does not constrain output to a JSON schema server-side, so a\n // `responseFormat: json` request can't be honored. Warn rather than\n // silently mislead — schema-constrained output should go through the\n // provider (@coder/ai-sdk-provider) instead.\n const warnings: SharedV4Warning[] = [];\n if (options.responseFormat?.type === \"json\") {\n warnings.push({\n type: \"unsupported\",\n feature: \"responseFormat\",\n details:\n \"Coder Agents does not enforce a JSON schema server-side, so structured output is best-effort (not schema-constrained). For reliable structured output, use @coder/ai-sdk-provider (createCoder) with generateObject / Output.object.\",\n });\n }\n yield { type: \"stream-start\", warnings };\n\n const action = classifyTurnAction(prompt);\n if (action.kind === \"noop\") {\n throw new CoderAgentError(\n \"CoderAgent received a prompt with no user message or tool results to act on.\",\n );\n }\n\n let afterId: number | undefined;\n\n // A fresh instance resuming an existing chat (config.chatId) has no\n // message cursor yet. Without one, a queued submission or a tool-result\n // resume falls back to afterId 0: the server replays the chat's FULL\n // history and this turn would absorb every earlier turn's content and\n // usage. Seed the cursor from the chat's newest message instead (the\n // messages endpoint pages newest-first; an empty chat has nothing to\n // replay, so 0 stays correct there).\n if (this.#chatId && this.#lastSeenMessageId === 0) {\n const { messages } = await this.#config.client.getMessages(\n this.#chatId,\n { limit: 1 },\n signal,\n );\n this.#lastSeenMessageId = messages[0]?.id ?? 0;\n }\n\n if (action.kind === \"new-turn\") {\n // Resolve the model config and upload any file parts concurrently — they\n // are independent round-trips. (Uploads run before the chat exists and\n // resolve file parts to `file` input parts referencing their uploaded ids.)\n const [modelConfigId, content] = await Promise.all([\n this.#resolveModelConfigId(signal),\n this.#buildContent(action.content, signal),\n ]);\n if (!this.#chatId) {\n const req: CreateChatRequest = {\n organization_id: this.#config.organizationId,\n content,\n client_type: \"api\",\n };\n const system = extractSystemPrompt(prompt);\n if (system) req.system_prompt = system;\n const tools = toolsToDynamicTools(options.tools);\n if (tools.length > 0) req.unsafe_dynamic_tools = tools;\n if (modelConfigId) req.model_config_id = modelConfigId;\n if (this.#config.workspaceId) req.workspace_id = this.#config.workspaceId;\n if (this.#config.mcpServerIds?.length) req.mcp_server_ids = this.#config.mcpServerIds;\n if (this.#config.planMode) req.plan_mode = this.#config.planMode;\n const chat = await this.#config.client.createChat(req, signal);\n this.#chatId = chat.id;\n afterId = this.#lastSeenMessageId > 0 ? this.#lastSeenMessageId : undefined;\n } else {\n const resp = await this.#config.client.createChatMessage(\n this.#chatId,\n {\n content,\n ...(modelConfigId ? { model_config_id: modelConfigId } : {}),\n },\n signal,\n );\n afterId = resp.message?.id ?? this.#lastSeenMessageId;\n }\n } else {\n // resume\n if (!this.#chatId)\n throw new CoderChatError({ message: \"cannot submit tool results before a chat exists\" });\n const fresh = action.toolResults.filter(\n (r) => !this.#submittedToolCallIds.has(r.tool_call_id),\n );\n if (fresh.length > 0) {\n await this.#config.client.submitToolResults(this.#chatId, { results: fresh }, signal);\n for (const r of fresh) this.#submittedToolCallIds.add(r.tool_call_id);\n }\n afterId = this.#lastSeenMessageId;\n }\n\n const chatId = this.#chatId as string;\n // Only messages past the turn's starting cursor belong to this turn —\n // resuming a chat replays earlier turns' messages (usage included), and\n // a mid-turn history reset re-sends them again. Constructing the\n // translator with the cursor makes it impossible to ingest before the\n // boundary is known.\n translator = new TurnTranslator({\n dynamicToolNames: dynamicToolNames(options.tools),\n turnCursor: afterId ?? 0,\n });\n // chatd emits the `requires_action` status BEFORE the `action_required`\n // event that carries the pending tool calls, so for that status we keep\n // reading until the client tool calls have actually been emitted (bounded\n // by a safety counter, since the stream is a live subscription).\n let sinceRequiresAction = 0;\n try {\n for await (const ev of this.#config.client.streamEvents(chatId, { afterId, signal })) {\n for (const part of translator.ingest(ev)) yield part;\n const status = translator.terminalStatus;\n if (status) {\n if (status !== \"requires_action\") break;\n if (translator.clientToolCallSeen) break;\n if (++sinceRequiresAction > 200) break;\n }\n }\n } catch (err) {\n // Abort surfaces here only if the reader threw instead of closing cleanly;\n // prefer the abort/timeout classification.\n throwIfAborted();\n // The reader only throws transport-level CoderAgentErrors (socket error /\n // unparseable frame). Surface them as a retryable stream failure so a\n // caller's `CoderChatError && retryable` retry path catches a dropped\n // connection instead of seeing a bare, non-retryable error.\n if (\n err instanceof CoderAgentError &&\n !(err instanceof CoderApiError) &&\n !(err instanceof CoderChatError)\n ) {\n throw new CoderChatError({\n message: `Coder chat stream failed mid-turn: ${err.message}`,\n kind: \"stream_closed\",\n retryable: true,\n });\n }\n throw err;\n }\n\n // The stream loop exits cleanly when the socket closes on abort, so classify\n // an abort/timeout here before treating the end as a normal/closed turn.\n throwIfAborted();\n\n // No terminal status: the stream ended before the turn settled. If an\n // `error` event arrived (without a trailing `status: error`), fall through to\n // finish() so the real error surfaces (unified:\"error\" + the error part),\n // consistent with the `status: error` path; otherwise it's a genuine\n // premature close — surface it rather than a clean (truncated) `stop`.\n if (!translator.terminalStatus && !translator.error) {\n throw new CoderChatError({\n message:\n \"Coder chat stream ended before the turn settled (connection closed or the server ended the stream without a terminal status).\",\n kind: \"stream_closed\",\n retryable: true,\n });\n }\n\n for (const part of translator.finish()) yield part;\n } catch (err) {\n // A timeout/caller abort during the REST phase (createChat / message /\n // tool-results / model resolution) rejects the fetch before the stream loop;\n // reclassify so the documented retryable timeout/abort contract still holds.\n throwIfAborted();\n throw err;\n } finally {\n // Advance the cursor on every exit (success, abort, error) so resuming the\n // same chat doesn't re-read messages already streamed this turn.\n if (translator && translator.maxMessageId > this.#lastSeenMessageId)\n this.#lastSeenMessageId = translator.maxMessageId;\n signal?.removeEventListener(\"abort\", interrupt);\n // Teardown of an unsettled turn — stream cancel(), premature close, or an\n // abort the listener didn't cover — interrupt the server so it stops.\n // (A turn that failed before streaming has no translator and never\n // settled, so it interrupts too.)\n if (!translator?.terminalStatus) interrupt();\n this.#inFlight = false;\n }\n }\n\n async doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult> {\n // A consumer can tear the stream down via ReadableStream.cancel() without\n // aborting options.abortSignal. Route cancel through an abort so the turn\n // interrupts the server run and the blocked stream reader unblocks — a bare\n // gen.return() would deadlock on a pending socket read and never reach the\n // interrupt, leaking the workspace.\n const cancelController = new AbortController();\n const abortSignal = options.abortSignal\n ? AbortSignal.any([options.abortSignal, cancelController.signal])\n : cancelController.signal;\n const gen = this.#runTurn({ ...options, abortSignal });\n const stream = new ReadableStream<LanguageModelV4StreamPart>({\n async pull(controller) {\n try {\n const { value, done } = await gen.next();\n if (done) controller.close();\n else controller.enqueue(value);\n } catch (err) {\n // A consumer-initiated cancel aborts the turn (to interrupt the server\n // and unblock the reader); that surfaces here as the turn's AbortError,\n // but it's an intentional teardown, so end the stream cleanly rather\n // than erroring it. A caller's own abortSignal still errors as usual.\n if (cancelController.signal.aborted) controller.close();\n else controller.error(err);\n }\n },\n async cancel() {\n cancelController.abort();\n await gen.return().catch(() => {});\n },\n });\n return { stream };\n }\n\n async doGenerate(options: LanguageModelV4CallOptions): Promise<LanguageModelV4GenerateResult> {\n const content: LanguageModelV4Content[] = [];\n const textBuf = new Map<string, string>();\n const reasoningBuf = new Map<string, string>();\n let usage: LanguageModelV4Usage = EMPTY_USAGE;\n let finishReason: LanguageModelV4GenerateResult[\"finishReason\"] = {\n unified: \"stop\",\n raw: undefined,\n };\n let providerMetadata: LanguageModelV4GenerateResult[\"providerMetadata\"];\n const warnings: LanguageModelV4GenerateResult[\"warnings\"] = [];\n\n for await (const part of this.#runTurn(options)) {\n switch (part.type) {\n case \"stream-start\":\n warnings.push(...part.warnings);\n break;\n case \"text-start\":\n textBuf.set(part.id, \"\");\n break;\n case \"text-delta\":\n textBuf.set(part.id, (textBuf.get(part.id) ?? \"\") + part.delta);\n break;\n case \"text-end\": {\n const t = textBuf.get(part.id) ?? \"\";\n if (t.length > 0) content.push({ type: \"text\", text: t });\n textBuf.delete(part.id);\n break;\n }\n case \"reasoning-start\":\n reasoningBuf.set(part.id, \"\");\n break;\n case \"reasoning-delta\":\n reasoningBuf.set(part.id, (reasoningBuf.get(part.id) ?? \"\") + part.delta);\n break;\n case \"reasoning-end\": {\n const t = reasoningBuf.get(part.id) ?? \"\";\n if (t.length > 0) content.push({ type: \"reasoning\", text: t });\n reasoningBuf.delete(part.id);\n break;\n }\n case \"tool-call\":\n case \"tool-result\":\n case \"source\":\n case \"file\":\n content.push(part);\n break;\n case \"finish\":\n usage = part.usage;\n finishReason = part.finishReason;\n providerMetadata = part.providerMetadata;\n break;\n case \"error\":\n throw part.error instanceof Error\n ? part.error\n : new CoderChatError({ message: String(part.error) });\n default:\n break;\n }\n }\n\n return {\n content,\n finishReason,\n usage,\n warnings,\n ...(providerMetadata ? { providerMetadata } : {}),\n };\n }\n}\n","import type { DynamicToolUIPart, UIMessage } from \"ai\";\nimport type { ChatMessage, ChatMessagePart } from \"../coder/types.js\";\n\n/** Options for {@link chatMessagesToUIMessages}. */\nexport interface ChatMessagesToUIMessagesOptions {\n /**\n * Resolves a persisted `file` part to a URL for its `FileUIPart`. Persisted\n * file parts carry only a `file_id` (no bytes, and usually no `url`), so\n * without a URL there is nothing for the UI to render and the part is\n * skipped. Download the bytes with `CoderChatClient.getChatFile(fileId)`\n * and return a data:/object/proxy URL here to keep attachments visible.\n * Returning `undefined` falls back to the part's own `url`, if any.\n */\n fileUrl?: (part: ChatMessagePart) => string | undefined;\n}\n\nfunction toolErrorText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (result === undefined || result === null) return \"\";\n return JSON.stringify(result) ?? \"\";\n}\n\nfunction toDynamicToolPart(\n call: ChatMessagePart,\n result: ChatMessagePart | undefined,\n): DynamicToolUIPart | undefined {\n const toolCallId = call.tool_call_id;\n const toolName = call.tool_name;\n if (!toolCallId || !toolName) return undefined;\n const providerExecuted = call.provider_executed ?? result?.provider_executed;\n const base = {\n type: \"dynamic-tool\" as const,\n toolName,\n toolCallId,\n ...(providerExecuted !== undefined ? { providerExecuted } : {}),\n };\n if (!result) return { ...base, state: \"input-available\", input: call.args };\n if (result.is_error) {\n return {\n ...base,\n state: \"output-error\",\n input: call.args,\n errorText: toolErrorText(result.result),\n };\n }\n return { ...base, state: \"output-available\", input: call.args, output: result.result };\n}\n\nfunction toUIParts(\n content: ChatMessagePart[],\n resultsByCallId: ReadonlyMap<string, ChatMessagePart>,\n opts: ChatMessagesToUIMessagesOptions | undefined,\n): UIMessage[\"parts\"] {\n const parts: UIMessage[\"parts\"] = [];\n for (const part of content) {\n switch (part.type) {\n case \"text\":\n parts.push({ type: \"text\", text: part.text ?? \"\", state: \"done\" });\n break;\n case \"reasoning\":\n parts.push({ type: \"reasoning\", text: part.text ?? \"\", state: \"done\" });\n break;\n case \"tool-call\": {\n const result = part.tool_call_id ? resultsByCallId.get(part.tool_call_id) : undefined;\n const ui = toDynamicToolPart(part, result);\n if (ui) parts.push(ui);\n break;\n }\n case \"source\":\n // `sourceId` is required by the AI SDK; fall back to the URL when the\n // wire part has no `source_id`. Without a URL there is nothing to\n // link, so the part is skipped.\n if (part.url) {\n parts.push({\n type: \"source-url\",\n sourceId: part.source_id || part.url,\n url: part.url,\n ...(part.title ? { title: part.title } : {}),\n });\n }\n break;\n case \"file\": {\n const url = opts?.fileUrl?.(part) ?? part.url;\n if (url) {\n parts.push({\n type: \"file\",\n mediaType: part.media_type ?? \"application/octet-stream\",\n ...(part.name ? { filename: part.name } : {}),\n url,\n });\n }\n break;\n }\n case \"tool-result\":\n // Folded into the originating `dynamic-tool` part above.\n break;\n default:\n // file-reference / context-file / skill / future kinds: no UI\n // equivalent; skip so history written by newer Coder servers\n // degrades gracefully.\n break;\n }\n }\n return parts;\n}\n\n/**\n * Converts persisted chat history (`CoderChatClient.getMessages`) into AI SDK\n * v6 `UIMessage`s, e.g. to rehydrate `useChat({ messages })` for an existing\n * chat. Pure and side-effect free.\n *\n * The mapping mirrors what a live-streamed transcript of the same turn looks\n * like, with one caveat around client-tool typing (below):\n * - One `ChatMessage` becomes one `UIMessage` (ids stringified), sorted\n * chronologically by id regardless of input order — the messages endpoint\n * pages newest-first by default, while `useChat` needs oldest-first. The\n * exception is `role: \"tool\"` messages: their `tool-result` parts are\n * folded into the originating assistant `dynamic-tool` part and the message\n * itself is dropped.\n * - `text`/`reasoning` parts map to their UI counterparts with\n * `state: \"done\"` (history is complete, never streaming).\n * - Every `tool-call` part becomes a `dynamic-tool` part, in state\n * `\"output-available\"` once a result exists (`\"output-error\"` when it is\n * marked `is_error`), or `\"input-available\"` while no result has been\n * persisted yet. For server-executed tools this matches the live stream\n * (they stream with `dynamic: true`); client (`ToolSet`) tools stream live\n * as statically-typed `tool-{name}` parts, but persisted history does not\n * record which names were client tools, so they rehydrate as `dynamic-tool`\n * too. Render tools by name — ai's `isToolOrDynamicToolUIPart` +\n * `getToolOrDynamicToolName` — rather than exact `part.type` matches and\n * both shapes look identical.\n * - `source` parts become `source-url` parts; skipped when the wire part has\n * no `url`.\n * - `file` parts become `file` UI parts only when a URL is available (see\n * {@link ChatMessagesToUIMessagesOptions.fileUrl}): persisted file parts\n * carry only a `file_id`, and `FileUIPart` requires a `url`. Parts without\n * one are skipped.\n * - `file-reference` / `context-file` / `skill` / unknown part kinds have no\n * UI equivalent and are skipped silently (forward compatible).\n */\nexport function chatMessagesToUIMessages(\n messages: ChatMessage[],\n opts?: ChatMessagesToUIMessagesOptions,\n): UIMessage[] {\n // Normalize to chronological order (ids are per-chat sequential). The\n // messages endpoint pages newest-first by default (only `after_id` queries\n // return ascending), and `useChat` expects oldest-first — accepting any\n // input order makes both pagination shapes safe to pass straight in.\n const ordered = [...messages].sort((a, b) => a.id - b.id);\n\n // Pass 1: index tool results by call id. chatd persists results as separate\n // role:\"tool\" messages; scan every message so results inlined on assistant\n // snapshots fold identically.\n const resultsByCallId = new Map<string, ChatMessagePart>();\n for (const message of ordered) {\n for (const part of message.content ?? []) {\n if (part.type !== \"tool-result\" || !part.tool_call_id) continue;\n if (!resultsByCallId.has(part.tool_call_id)) resultsByCallId.set(part.tool_call_id, part);\n }\n }\n\n // Pass 2: one UIMessage per ChatMessage, chronological. role:\"tool\" messages\n // were folded above and produce none.\n const out: UIMessage[] = [];\n for (const message of ordered) {\n if (message.role === \"tool\") continue;\n out.push({\n id: String(message.id),\n role: message.role,\n parts: toUIParts(message.content ?? [], resultsByCallId, opts),\n });\n }\n return out;\n}\n"],"mappings":"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","names":["sleep"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coder/ai-sdk-agent",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "A Vercel AI SDK-compliant agent backed by Coder Agents, Coder's server-side agent runtime. `new CoderAgent()` returns a real AI SDK Agent (generate/stream).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"access": "public"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"ws": "^8.
|
|
45
|
+
"ws": "^8.21.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@ai-sdk/provider": "4.0.
|
|
49
|
-
"@ai-sdk/react": "4.0.
|
|
50
|
-
"@types/node": "^
|
|
48
|
+
"@ai-sdk/provider": "4.0.7",
|
|
49
|
+
"@ai-sdk/react": "4.0.69",
|
|
50
|
+
"@types/node": "^26.2.0",
|
|
51
51
|
"@types/ws": "^8.18.1",
|
|
52
|
-
"ai": "^7.0.
|
|
52
|
+
"ai": "^7.0.66",
|
|
53
53
|
"tsup": "^8.5.1",
|
|
54
|
-
"tsx": "^4.
|
|
54
|
+
"tsx": "^4.23.12",
|
|
55
55
|
"typescript": "^6.0.3",
|
|
56
|
-
"vitest": "^4.1.
|
|
56
|
+
"vitest": "^4.1.10",
|
|
57
57
|
"zod": "4.4.3"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|