@qoder-ai/qoder-agent-sdk 1.0.4 → 1.0.6
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 +119 -73
- package/dist/browser/index.js +3 -3
- package/dist/browser/websocket-transport.d.ts +3 -1
- package/dist/core/default-runtime.d.ts +8 -0
- package/dist/core/process-transport.d.ts +5 -31
- package/dist/core/query-transport.d.ts +6 -0
- package/dist/core/runtime-launch-options.d.ts +84 -0
- package/dist/core/transport.d.ts +15 -0
- package/dist/core/worker-transport.d.ts +86 -0
- package/dist/direct-connect/direct-connect-transport.d.ts +3 -1
- package/dist/global/plugins.d.ts +14 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.js +22 -20
- package/dist/protocol/common.d.ts +55 -0
- package/dist/protocol/control.d.ts +8 -1
- package/dist/protocol/index.d.ts +2 -2
- package/dist/protocol/index.js +1 -1
- package/dist/query/query-runner.d.ts +2 -0
- package/dist/query/query.d.ts +1 -1
- package/dist/runtime-manifest.json +8 -0
- package/dist/types/common.d.ts +1 -1
- package/dist/types/control.d.ts +1 -1
- package/dist/types/index.d.ts +6 -1
- package/dist/types/options.d.ts +15 -0
- package/dist/types/plugins.d.ts +41 -0
- package/package.json +5 -2
- package/scripts/postinstall.cjs +376 -19
package/README.md
CHANGED
|
@@ -21,27 +21,75 @@ Prerequisites:
|
|
|
21
21
|
`zod` is a peer dependency used when you define MCP tool schemas. Keep it
|
|
22
22
|
installed if your application uses SDK tools or MCP integration.
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Runtime Behavior
|
|
25
25
|
|
|
26
|
-
The npm package
|
|
26
|
+
The standard npm package keeps the existing process runtime behavior:
|
|
27
|
+
`query()` launches `qodercli` as a child process by default. During
|
|
27
28
|
`npm install`, the postinstall script downloads the platform-specific CLI into
|
|
28
29
|
`dist/_bundled`.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
Packages built with the Worker runtime bundle bake that choice into the SDK:
|
|
32
|
+
their default `query()` transport is `WorkerTransport.default`, and install
|
|
33
|
+
does not download the process CLI unless `QODER_INSTALL_BUNDLED_CLI=1` is set.
|
|
34
|
+
Worker runtime assets can either be packaged into the tarball or downloaded by
|
|
35
|
+
postinstall, depending on the release pipeline.
|
|
36
|
+
|
|
37
|
+
You can still select a transport explicitly on the same `query()` entry:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import {
|
|
41
|
+
qodercliAuth,
|
|
42
|
+
query,
|
|
43
|
+
WorkerTransport,
|
|
44
|
+
} from "@qoder-ai/qoder-agent-sdk";
|
|
45
|
+
|
|
46
|
+
const q = query({
|
|
47
|
+
prompt: "Summarize this repository.",
|
|
48
|
+
options: {
|
|
49
|
+
auth: qodercliAuth(),
|
|
50
|
+
transport: WorkerTransport.default,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When the package includes `dist/_worker/qoder-worker-runtime.obf.mjs`, the
|
|
56
|
+
worker transport uses that embedded runtime; otherwise set
|
|
57
|
+
`QODER_WORKER_RUNTIME_PATH` or pass
|
|
58
|
+
`new WorkerTransport({ pathToQoderWorkerRuntime })`.
|
|
59
|
+
|
|
60
|
+
Worker-bundled packages include a `dist/runtime-manifest.json` that records the
|
|
61
|
+
default runtime and whether the Worker runtime is packaged or installed by
|
|
62
|
+
postinstall. Install-delivered packages can also pin the qodercli Worker runtime
|
|
63
|
+
archive URL in that manifest; the SDK package itself does not need to host a
|
|
64
|
+
copy of the archive. The presence of `dist/_worker` is used as an integrity
|
|
65
|
+
check after installation, not as the mode selector.
|
|
66
|
+
|
|
67
|
+
The packaged Worker runtime is built as a self-contained universal profile by
|
|
68
|
+
default. Runtime assets such as native keychain bindings, PTY bindings, image
|
|
69
|
+
processing bindings, devtools support, and ripgrep binaries are carried by the
|
|
70
|
+
SDK package instead of by the host product.
|
|
71
|
+
|
|
72
|
+
If your environment blocks install scripts or network downloads, skip postinstall
|
|
73
|
+
runtime downloads:
|
|
32
74
|
|
|
33
75
|
```bash
|
|
34
76
|
QODER_SKIP_DOWNLOAD=1 npm install @qoder-ai/qoder-agent-sdk zod
|
|
35
77
|
```
|
|
36
78
|
|
|
37
|
-
|
|
79
|
+
`QODER_SKIP_DOWNLOAD` only affects install-time downloads. It does not change the
|
|
80
|
+
package's default runtime. Then point the process transport at an existing
|
|
81
|
+
`qodercli`, or point the worker transport at an existing runtime:
|
|
38
82
|
|
|
39
83
|
```bash
|
|
40
84
|
export QODERCLI_PATH=/absolute/path/to/qodercli
|
|
85
|
+
# or
|
|
86
|
+
export QODER_WORKER_RUNTIME_PATH=/absolute/path/to/qoder-worker-runtime.obf.mjs
|
|
41
87
|
```
|
|
42
88
|
|
|
43
|
-
|
|
44
|
-
`
|
|
89
|
+
`query()` is the only query entry point. Standard packages default to process;
|
|
90
|
+
Worker-bundled packages default to Worker. Pass `transport: ProcessTransport.default`,
|
|
91
|
+
`transport: WorkerTransport.default`, or a custom transport provider when a
|
|
92
|
+
single call needs to override the package default.
|
|
45
93
|
|
|
46
94
|
## Authentication
|
|
47
95
|
|
|
@@ -50,10 +98,10 @@ Every SDK query needs an explicit authentication option.
|
|
|
50
98
|
To reuse the local `qodercli` login state:
|
|
51
99
|
|
|
52
100
|
```ts
|
|
53
|
-
import { qodercliAuth, query } from
|
|
101
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
54
102
|
|
|
55
103
|
const q = query({
|
|
56
|
-
prompt:
|
|
104
|
+
prompt: "Summarize this repository.",
|
|
57
105
|
options: {
|
|
58
106
|
auth: qodercliAuth(),
|
|
59
107
|
cwd: process.cwd(),
|
|
@@ -81,10 +129,10 @@ export QODER_PERSONAL_ACCESS_TOKEN=your-token
|
|
|
81
129
|
```
|
|
82
130
|
|
|
83
131
|
```ts
|
|
84
|
-
import { accessTokenFromEnv, query } from
|
|
132
|
+
import { accessTokenFromEnv, query } from "@qoder-ai/qoder-agent-sdk";
|
|
85
133
|
|
|
86
134
|
const q = query({
|
|
87
|
-
prompt:
|
|
135
|
+
prompt: "Summarize this repository.",
|
|
88
136
|
options: {
|
|
89
137
|
auth: accessTokenFromEnv(),
|
|
90
138
|
cwd: process.cwd(),
|
|
@@ -99,10 +147,10 @@ const q = query({
|
|
|
99
147
|
Create `demo.mjs`:
|
|
100
148
|
|
|
101
149
|
```js
|
|
102
|
-
import { qodercliAuth, query } from
|
|
150
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
103
151
|
|
|
104
152
|
const q = query({
|
|
105
|
-
prompt: process.argv.slice(2).join(
|
|
153
|
+
prompt: process.argv.slice(2).join(" ") || "Explain what this project does.",
|
|
106
154
|
options: {
|
|
107
155
|
auth: qodercliAuth(),
|
|
108
156
|
cwd: process.cwd(),
|
|
@@ -130,25 +178,25 @@ node demo.mjs "List the important files in this repository."
|
|
|
130
178
|
messages.
|
|
131
179
|
|
|
132
180
|
```js
|
|
133
|
-
import { qodercliAuth, query } from
|
|
181
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
134
182
|
|
|
135
183
|
const q = query({
|
|
136
|
-
prompt:
|
|
184
|
+
prompt: "Explain this repository.",
|
|
137
185
|
options: {
|
|
138
186
|
auth: qodercliAuth(),
|
|
139
187
|
cwd: process.cwd(),
|
|
140
|
-
systemPrompt:
|
|
188
|
+
systemPrompt: "You are a helpful assistant.",
|
|
141
189
|
maxTurns: 1,
|
|
142
190
|
},
|
|
143
191
|
});
|
|
144
192
|
|
|
145
193
|
for await (const message of q) {
|
|
146
|
-
if (message.type !==
|
|
194
|
+
if (message.type !== "assistant") {
|
|
147
195
|
continue;
|
|
148
196
|
}
|
|
149
197
|
|
|
150
198
|
for (const block of message.message.content) {
|
|
151
|
-
if (block.type ===
|
|
199
|
+
if (block.type === "text") {
|
|
152
200
|
console.log(block.text);
|
|
153
201
|
}
|
|
154
202
|
}
|
|
@@ -164,16 +212,16 @@ auto-approved, while unlisted tools continue through `permissionMode` and
|
|
|
164
212
|
available toolset. To block tools, use `disallowedTools`.
|
|
165
213
|
|
|
166
214
|
```ts
|
|
167
|
-
import { qodercliAuth, query } from
|
|
215
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
168
216
|
|
|
169
217
|
const q = query({
|
|
170
|
-
prompt:
|
|
218
|
+
prompt: "Update the README introduction.",
|
|
171
219
|
options: {
|
|
172
220
|
auth: qodercliAuth(),
|
|
173
221
|
cwd: process.cwd(),
|
|
174
|
-
allowedTools: [
|
|
175
|
-
disallowedTools: [
|
|
176
|
-
permissionMode:
|
|
222
|
+
allowedTools: ["Read", "Edit"],
|
|
223
|
+
disallowedTools: ["Bash"],
|
|
224
|
+
permissionMode: "acceptEdits",
|
|
177
225
|
},
|
|
178
226
|
});
|
|
179
227
|
```
|
|
@@ -181,22 +229,22 @@ const q = query({
|
|
|
181
229
|
For application-specific approval flows, provide `canUseTool`:
|
|
182
230
|
|
|
183
231
|
```ts
|
|
184
|
-
import { qodercliAuth, query } from
|
|
232
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
185
233
|
|
|
186
234
|
const q = query({
|
|
187
|
-
prompt:
|
|
235
|
+
prompt: "Inspect the source code, but do not run shell commands.",
|
|
188
236
|
options: {
|
|
189
237
|
auth: qodercliAuth(),
|
|
190
238
|
cwd: process.cwd(),
|
|
191
239
|
canUseTool: async (toolName) => {
|
|
192
|
-
if (toolName ===
|
|
240
|
+
if (toolName === "Bash") {
|
|
193
241
|
return {
|
|
194
|
-
behavior:
|
|
195
|
-
message:
|
|
242
|
+
behavior: "deny",
|
|
243
|
+
message: "Shell commands are disabled here.",
|
|
196
244
|
};
|
|
197
245
|
}
|
|
198
246
|
|
|
199
|
-
return { behavior:
|
|
247
|
+
return { behavior: "allow" };
|
|
200
248
|
},
|
|
201
249
|
},
|
|
202
250
|
});
|
|
@@ -207,13 +255,13 @@ const q = query({
|
|
|
207
255
|
Use `cwd` to run the agent in a specific project directory:
|
|
208
256
|
|
|
209
257
|
```ts
|
|
210
|
-
import { qodercliAuth, query } from
|
|
258
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
211
259
|
|
|
212
260
|
const q = query({
|
|
213
|
-
prompt:
|
|
261
|
+
prompt: "Analyze this project.",
|
|
214
262
|
options: {
|
|
215
263
|
auth: qodercliAuth(),
|
|
216
|
-
cwd:
|
|
264
|
+
cwd: "/path/to/project",
|
|
217
265
|
},
|
|
218
266
|
});
|
|
219
267
|
```
|
|
@@ -224,19 +272,19 @@ Use an async iterable prompt when you need a long-lived, bidirectional session
|
|
|
224
272
|
instead of a single string prompt.
|
|
225
273
|
|
|
226
274
|
```ts
|
|
227
|
-
import { qodercliAuth, query } from
|
|
275
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
228
276
|
|
|
229
277
|
function userMessage(text: string) {
|
|
230
278
|
return {
|
|
231
|
-
type:
|
|
232
|
-
message: { role:
|
|
279
|
+
type: "user",
|
|
280
|
+
message: { role: "user", content: [{ type: "text", text }] },
|
|
233
281
|
parent_tool_use_id: null,
|
|
234
282
|
};
|
|
235
283
|
}
|
|
236
284
|
|
|
237
285
|
async function* conversation() {
|
|
238
|
-
yield userMessage(
|
|
239
|
-
yield userMessage(
|
|
286
|
+
yield userMessage("Inspect this project and summarize the main modules.");
|
|
287
|
+
yield userMessage("Now list the files that are most likely to need review.");
|
|
240
288
|
}
|
|
241
289
|
|
|
242
290
|
const q = query({
|
|
@@ -265,33 +313,33 @@ SDK MCP servers. This avoids managing a separate MCP subprocess for simple
|
|
|
265
313
|
application-local tools.
|
|
266
314
|
|
|
267
315
|
```ts
|
|
268
|
-
import {
|
|
269
|
-
|
|
316
|
+
import {
|
|
317
|
+
createSdkMcpServer,
|
|
318
|
+
qodercliAuth,
|
|
319
|
+
query,
|
|
320
|
+
tool,
|
|
321
|
+
} from "@qoder-ai/qoder-agent-sdk";
|
|
322
|
+
import { z } from "zod";
|
|
270
323
|
|
|
271
324
|
const server = createSdkMcpServer({
|
|
272
|
-
name:
|
|
273
|
-
version:
|
|
325
|
+
name: "my-tools",
|
|
326
|
+
version: "1.0.0",
|
|
274
327
|
tools: [
|
|
275
|
-
tool(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
{ name: z.string() },
|
|
279
|
-
async ({ name }) => ({
|
|
280
|
-
content: [{ type: 'text', text: `Hello, ${name}!` }],
|
|
281
|
-
}),
|
|
282
|
-
),
|
|
328
|
+
tool("greet", "Greet a user.", { name: z.string() }, async ({ name }) => ({
|
|
329
|
+
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
330
|
+
})),
|
|
283
331
|
],
|
|
284
332
|
});
|
|
285
333
|
|
|
286
334
|
const q = query({
|
|
287
|
-
prompt:
|
|
335
|
+
prompt: "Greet Alice.",
|
|
288
336
|
options: {
|
|
289
337
|
auth: qodercliAuth(),
|
|
290
338
|
cwd: process.cwd(),
|
|
291
339
|
mcpServers: {
|
|
292
340
|
tools: server,
|
|
293
341
|
},
|
|
294
|
-
allowedTools: [
|
|
342
|
+
allowedTools: ["mcp__tools__greet"],
|
|
295
343
|
},
|
|
296
344
|
});
|
|
297
345
|
```
|
|
@@ -303,26 +351,26 @@ They are useful for validation, policy checks, logging, and
|
|
|
303
351
|
application-specific feedback.
|
|
304
352
|
|
|
305
353
|
```ts
|
|
306
|
-
import { qodercliAuth, query } from
|
|
307
|
-
import type { HookCallback } from
|
|
354
|
+
import { qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
355
|
+
import type { HookCallback } from "@qoder-ai/qoder-agent-sdk";
|
|
308
356
|
|
|
309
357
|
const blockDeployScript: HookCallback = async (input) => {
|
|
310
|
-
if (input.hook_event_name !==
|
|
358
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
311
359
|
return {};
|
|
312
360
|
}
|
|
313
361
|
|
|
314
|
-
if (input.tool_name !==
|
|
362
|
+
if (input.tool_name !== "Bash") {
|
|
315
363
|
return {};
|
|
316
364
|
}
|
|
317
365
|
|
|
318
366
|
const toolInput = input.tool_input as { command?: unknown } | undefined;
|
|
319
|
-
const command = String(toolInput?.command ??
|
|
320
|
-
if (command.includes(
|
|
367
|
+
const command = String(toolInput?.command ?? "");
|
|
368
|
+
if (command.includes("./deploy.sh")) {
|
|
321
369
|
return {
|
|
322
370
|
hookSpecificOutput: {
|
|
323
|
-
hookEventName:
|
|
324
|
-
permissionDecision:
|
|
325
|
-
permissionDecisionReason:
|
|
371
|
+
hookEventName: "PreToolUse",
|
|
372
|
+
permissionDecision: "deny",
|
|
373
|
+
permissionDecisionReason: "Deployment scripts require review.",
|
|
326
374
|
},
|
|
327
375
|
};
|
|
328
376
|
}
|
|
@@ -331,14 +379,12 @@ const blockDeployScript: HookCallback = async (input) => {
|
|
|
331
379
|
};
|
|
332
380
|
|
|
333
381
|
const q = query({
|
|
334
|
-
prompt:
|
|
382
|
+
prompt: "Review this project without deploying it.",
|
|
335
383
|
options: {
|
|
336
384
|
auth: qodercliAuth(),
|
|
337
385
|
cwd: process.cwd(),
|
|
338
386
|
hooks: {
|
|
339
|
-
PreToolUse: [
|
|
340
|
-
{ matcher: 'Bash', hooks: [blockDeployScript] },
|
|
341
|
-
],
|
|
387
|
+
PreToolUse: [{ matcher: "Bash", hooks: [blockDeployScript] }],
|
|
342
388
|
},
|
|
343
389
|
},
|
|
344
390
|
});
|
|
@@ -353,12 +399,12 @@ import {
|
|
|
353
399
|
ProtocolVersionMismatchError,
|
|
354
400
|
qodercliAuth,
|
|
355
401
|
query,
|
|
356
|
-
} from
|
|
402
|
+
} from "@qoder-ai/qoder-agent-sdk";
|
|
357
403
|
|
|
358
404
|
function errorCode(error: unknown): string | undefined {
|
|
359
|
-
if (typeof error ===
|
|
405
|
+
if (typeof error === "object" && error !== null && "code" in error) {
|
|
360
406
|
const code = (error as { code?: unknown }).code;
|
|
361
|
-
return typeof code ===
|
|
407
|
+
return typeof code === "string" ? code : undefined;
|
|
362
408
|
}
|
|
363
409
|
|
|
364
410
|
return undefined;
|
|
@@ -366,7 +412,7 @@ function errorCode(error: unknown): string | undefined {
|
|
|
366
412
|
|
|
367
413
|
try {
|
|
368
414
|
const q = query({
|
|
369
|
-
prompt:
|
|
415
|
+
prompt: "Hello Qoder",
|
|
370
416
|
options: {
|
|
371
417
|
auth: qodercliAuth(),
|
|
372
418
|
cwd: process.cwd(),
|
|
@@ -377,14 +423,14 @@ try {
|
|
|
377
423
|
console.log(message);
|
|
378
424
|
}
|
|
379
425
|
} catch (error) {
|
|
380
|
-
if (errorCode(error) ===
|
|
381
|
-
console.error(
|
|
426
|
+
if (errorCode(error) === "auth_not_configured") {
|
|
427
|
+
console.error("Configure options.auth before calling query().");
|
|
382
428
|
} else if (error instanceof ProtocolVersionMismatchError) {
|
|
383
|
-
console.error(
|
|
429
|
+
console.error("Upgrade qodercli or @qoder-ai/qoder-agent-sdk.");
|
|
384
430
|
} else if (error instanceof ModelPolicyTimeoutError) {
|
|
385
|
-
console.error(
|
|
431
|
+
console.error("Model policy resolution timed out.");
|
|
386
432
|
} else if (error instanceof AbortError) {
|
|
387
|
-
console.error(
|
|
433
|
+
console.error("The query was aborted.");
|
|
388
434
|
} else {
|
|
389
435
|
console.error(error);
|
|
390
436
|
}
|
package/dist/browser/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{McpServer as G}from"@modelcontextprotocol/sdk/server/mcp.js";function Z(s){if(!s.name||typeof s.name!="string"||s.name.trim()==="")throw new Error("[createSdkMcpServer] Server name must be a non-empty string.");let e=new G({name:s.name,version:s.version??"1.0.0"},{capabilities:{tools:s.tools&&s.tools.length>0?{}:void 0}});if(s.tools&&s.tools.length>0)for(let t of s.tools){if(!t.name||typeof t.name!="string"||t.name.trim()==="")throw new Error(`[createSdkMcpServer] Tool name must be a non-empty string in server "${s.name}".`);if(!t.description||typeof t.description!="string")throw new Error(`[createSdkMcpServer] Tool "${t.name}" must have a non-empty description string.`);try{e.registerTool(t.name,{description:t.description,inputSchema:t.inputSchema,...t.annotations?{annotations:t.annotations}:{}},t.handler)}catch(o){throw o instanceof Error&&o.message.includes("already registered")?new Error(`[createSdkMcpServer] Duplicate tool name "${t.name}" in server "${s.name}". Each tool name must be unique within a server.`):o}}return{type:"sdk",name:s.name,instance:e}}function X(s,e,t,o,r){return{name:s,description:e,inputSchema:t,handler:o,annotations:r?.annotations}}var v=class{queue=[];readResolve;readReject;isDone=!1;errorValue;started=!1;returnCallback;constructor(e){this.returnCallback=e}[Symbol.asyncIterator](){if(this.started)throw new Error("Stream can only be iterated once");return this.started=!0,this}async next(){if(this.queue.length>0)return{done:!1,value:this.queue.shift()};if(this.isDone)return{done:!0,value:void 0};if(this.errorValue!==void 0)throw this.errorValue;return new Promise((e,t)=>{this.readResolve=e,this.readReject=t})}enqueue(e){if(!this.isDone)if(this.readResolve){let t=this.readResolve;this.readResolve=void 0,this.readReject=void 0,t({done:!1,value:e})}else this.queue.push(e)}done(){if(this.isDone=!0,this.readResolve){let e=this.readResolve;this.readResolve=void 0,this.readReject=void 0,e({done:!0,value:void 0})}}error(e){if(this.errorValue=e,this.readReject){let t=this.readReject;this.readResolve=void 0,this.readReject=void 0,t(e)}}async return(){return this.isDone=!0,this.returnCallback?.(),{done:!0,value:void 0}}async throw(e){return this.error(e),{done:!0,value:void 0}}};var Y=3e4,ee=3e4,M=class{options;ws=null;ready=!1;readyResolve;readyReject;readyPromise;messages=new v;keepAliveTimer;exitError;constructor(e){this.options=e,this.readyPromise=new Promise((t,o)=>{this.readyResolve=t,this.readyReject=o}),this.initialize()}initialize(){let e=new globalThis.WebSocket(this.options.url);this.ws=e;let t=setTimeout(()=>{if(!this.ready){let o=new Error("WebSocket connection timeout");this.readyReject?.(o),e.close()}},ee);e.onopen=()=>{clearTimeout(t),this.ready=!0,this.readyResolve?.(),this.options.authMessage&&e.send(JSON.stringify(this.options.authMessage)+`
|
|
1
|
+
import{McpServer as G}from"@modelcontextprotocol/sdk/server/mcp.js";function Z(s){if(!s.name||typeof s.name!="string"||s.name.trim()==="")throw new Error("[createSdkMcpServer] Server name must be a non-empty string.");let e=new G({name:s.name,version:s.version??"1.0.0"},{capabilities:{tools:s.tools&&s.tools.length>0?{}:void 0}});if(s.tools&&s.tools.length>0)for(let t of s.tools){if(!t.name||typeof t.name!="string"||t.name.trim()==="")throw new Error(`[createSdkMcpServer] Tool name must be a non-empty string in server "${s.name}".`);if(!t.description||typeof t.description!="string")throw new Error(`[createSdkMcpServer] Tool "${t.name}" must have a non-empty description string.`);try{e.registerTool(t.name,{description:t.description,inputSchema:t.inputSchema,...t.annotations?{annotations:t.annotations}:{}},t.handler)}catch(o){throw o instanceof Error&&o.message.includes("already registered")?new Error(`[createSdkMcpServer] Duplicate tool name "${t.name}" in server "${s.name}". Each tool name must be unique within a server.`):o}}return{type:"sdk",name:s.name,instance:e}}function X(s,e,t,o,r){return{name:s,description:e,inputSchema:t,handler:o,annotations:r?.annotations}}var v=class{queue=[];readResolve;readReject;isDone=!1;errorValue;started=!1;returnCallback;constructor(e){this.returnCallback=e}[Symbol.asyncIterator](){if(this.started)throw new Error("Stream can only be iterated once");return this.started=!0,this}async next(){if(this.queue.length>0)return{done:!1,value:this.queue.shift()};if(this.isDone)return{done:!0,value:void 0};if(this.errorValue!==void 0)throw this.errorValue;return new Promise((e,t)=>{this.readResolve=e,this.readReject=t})}enqueue(e){if(!this.isDone)if(this.readResolve){let t=this.readResolve;this.readResolve=void 0,this.readReject=void 0,t({done:!1,value:e})}else this.queue.push(e)}done(){if(this.isDone=!0,this.readResolve){let e=this.readResolve;this.readResolve=void 0,this.readReject=void 0,e({done:!0,value:void 0})}}error(e){if(this.errorValue=e,this.readReject){let t=this.readReject;this.readResolve=void 0,this.readReject=void 0,t(e)}}async return(){return this.isDone=!0,this.returnCallback?.(),{done:!0,value:void 0}}async throw(e){return this.error(e),{done:!0,value:void 0}}};var Y=3e4,ee=3e4,M=class{options;ws=null;ready=!1;readyResolve;readyReject;readyPromise;messages=new v;keepAliveTimer;exitError;initialized=!1;constructor(e){this.options=e,this.readyPromise=new Promise((t,o)=>{this.readyResolve=t,this.readyReject=o}),this.initialize()}initialize(){return this.initialized||(this.initialized=!0,this.open()),this.readyPromise}open(){let e=new globalThis.WebSocket(this.options.url);this.ws=e;let t=setTimeout(()=>{if(!this.ready){let o=new Error("WebSocket connection timeout");this.readyReject?.(o),e.close()}},ee);e.onopen=()=>{clearTimeout(t),this.ready=!0,this.readyResolve?.(),this.options.authMessage&&e.send(JSON.stringify(this.options.authMessage)+`
|
|
2
2
|
`),this.keepAliveTimer=setInterval(()=>{e.readyState===WebSocket.OPEN&&e.send(JSON.stringify({type:"keep_alive"})+`
|
|
3
3
|
`)},Y)},e.onmessage=o=>{let r=String(o.data).split(`
|
|
4
|
-
`);for(let n of r)if(n.trim())try{let i=JSON.parse(n);this.messages.enqueue(i)}catch{}},e.onerror=()=>{let o=new Error("WebSocket connection error");this.exitError=o,this.readyReject?.(o),this.messages.done()},e.onclose=o=>{this.ready=!1,this.keepAliveTimer&&(clearInterval(this.keepAliveTimer),this.keepAliveTimer=void 0),o.code!==1e3&&o.code!==1001&&(this.exitError=new Error(`WebSocket closed abnormally: ${o.code} ${o.reason}`)),this.messages.done()},this.options.abortController?.signal.addEventListener("abort",()=>{this.close()})}async write(e){this.ready||await this.readyPromise,this.ws?.send(e)}close(){this.keepAliveTimer&&(clearInterval(this.keepAliveTimer),this.keepAliveTimer=void 0),this.ws&&(this.ws.close(1e3),this.ws=null),this.messages.done()}isReady(){return this.ready}async*readMessages(){if(yield*this.messages,this.exitError)throw this.exitError}endInput(){}};var k=class extends Error{constructor(e="Operation was aborted"){super(e),this.name="AbortError"}};var C=class extends Error{timeoutMs;constructor(e){super(`resolveModel did not return within ${e}ms. Increase resolveModelTimeoutMs or ensure the callback resolves faster.`),this.name="ModelPolicyTimeoutError",this.timeoutMs=e}},w=class extends Error{cliProtocolVersion;sdkProtocolVersion;constructor(e,t){super(`Wire protocol major mismatch: CLI announced "${e}" but this SDK was built for "${t}". Upgrade either qodercli or @qoder-ai/qoder-agent-sdk so the major versions align.`),this.name="ProtocolVersionMismatchError",this.cliProtocolVersion=e,this.sdkProtocolVersion=t}};var E="1.0.0";var q="ExitPlanMode";import*as B from"fs";function D(s,e,t,o){return new Promise((r,n)=>{if(!("id"in e&&e.id!==null)){try{t.onmessage?.(e)}catch(l){n(l);return}r({jsonrpc:"2.0"});return}let a=`${s}:${e.id}`;o.set(a,{resolve:r,reject:n}),t.onmessage?t.onmessage(e):(o.delete(a),n(new Error(`No message handler registered for MCP server "${s}"`)))})}function x(s,e,t){if("id"in e&&e.id!==null){let o=`${s}:${e.id}`,r=t.get(o);if(r)return r.resolve(e),t.delete(o),!0}return!1}function K(s,e,t){let o=0,r=`${s}:`,n=Array.from(t.entries());for(let i of n){let[a,l]=i;a.startsWith(r)&&(l.reject(e),t.delete(a),o++)}return o}function A(){return Math.random().toString(36).substring(2,15)}function j(s,e){let t="type"in e&&!("subtype"in e)?{...e,subtype:e.type}:e;return{type:"control_request",request_id:s,request:t}}function O(s,e){return{type:"control_response",response:{subtype:"success",request_id:s,response:e}}}function N(s,e,t){return{type:"control_response",response:{subtype:"error",request_id:s,error:e,...t?{code:t}:{}}}}function U(s){return typeof s=="object"&&s!==null&&s.type==="control_response"}function
|
|
4
|
+
`);for(let n of r)if(n.trim())try{let i=JSON.parse(n);this.messages.enqueue(i)}catch{}},e.onerror=()=>{let o=new Error("WebSocket connection error");this.exitError=o,this.readyReject?.(o),this.messages.done()},e.onclose=o=>{this.ready=!1,this.keepAliveTimer&&(clearInterval(this.keepAliveTimer),this.keepAliveTimer=void 0),o.code!==1e3&&o.code!==1001&&(this.exitError=new Error(`WebSocket closed abnormally: ${o.code} ${o.reason}`)),this.messages.done()},this.options.abortController?.signal.addEventListener("abort",()=>{this.close()})}async write(e){this.ready||await this.readyPromise,this.ws?.send(e)}close(){this.keepAliveTimer&&(clearInterval(this.keepAliveTimer),this.keepAliveTimer=void 0),this.ws&&(this.ws.close(1e3),this.ws=null),this.messages.done()}isReady(){return this.ready}async*readMessages(){if(yield*this.messages,this.exitError)throw this.exitError}endInput(){}};var k=class extends Error{constructor(e="Operation was aborted"){super(e),this.name="AbortError"}};var C=class extends Error{timeoutMs;constructor(e){super(`resolveModel did not return within ${e}ms. Increase resolveModelTimeoutMs or ensure the callback resolves faster.`),this.name="ModelPolicyTimeoutError",this.timeoutMs=e}},w=class extends Error{cliProtocolVersion;sdkProtocolVersion;constructor(e,t){super(`Wire protocol major mismatch: CLI announced "${e}" but this SDK was built for "${t}". Upgrade either qodercli or @qoder-ai/qoder-agent-sdk so the major versions align.`),this.name="ProtocolVersionMismatchError",this.cliProtocolVersion=e,this.sdkProtocolVersion=t}};var E="1.0.0";var q="ExitPlanMode";import*as B from"fs";function D(s,e,t,o){return new Promise((r,n)=>{if(!("id"in e&&e.id!==null)){try{t.onmessage?.(e)}catch(l){n(l);return}r({jsonrpc:"2.0"});return}let a=`${s}:${e.id}`;o.set(a,{resolve:r,reject:n}),t.onmessage?t.onmessage(e):(o.delete(a),n(new Error(`No message handler registered for MCP server "${s}"`)))})}function x(s,e,t){if("id"in e&&e.id!==null){let o=`${s}:${e.id}`,r=t.get(o);if(r)return r.resolve(e),t.delete(o),!0}return!1}function K(s,e,t){let o=0,r=`${s}:`,n=Array.from(t.entries());for(let i of n){let[a,l]=i;a.startsWith(r)&&(l.reject(e),t.delete(a),o++)}return o}function A(){return Math.random().toString(36).substring(2,15)}function j(s,e){let t="type"in e&&!("subtype"in e)?{...e,subtype:e.type}:e;return{type:"control_request",request_id:s,request:t}}function O(s,e){return{type:"control_response",response:{subtype:"success",request_id:s,response:e}}}function N(s,e,t){return{type:"control_response",response:{subtype:"error",request_id:s,error:e,...t?{code:t}:{}}}}function U(s){return typeof s=="object"&&s!==null&&s.type==="control_response"}function z(s){return typeof s=="object"&&s!==null&&s.type==="control_request"}function J(s){return typeof s=="object"&&s!==null&&(s.type==="control_cancel_request"||s.type==="control_cancel")}function P(s){if(typeof s!="object"||s===null)return;let e=s,t=e.subtype;if(typeof t=="string")return t;let o=e.type;return typeof o=="string"?o:void 0}function H(s){if(typeof s!="object"||s===null)return!1;let e=s.type;return e==="keepalive"||e==="keep_alive"}function $(s){return typeof s=="object"&&s!==null&&s.type==="streamlined_text"}function Q(s){return typeof s=="object"&&s!==null&&s.type==="streamlined_tool_use_summary"}function L(s){return typeof s=="object"&&s!==null&&s.type==="post_turn_summary"}function F(s){return typeof s=="object"&&s!==null&&s.type==="result"}var I=class s{static INITIALIZE_TIMEOUT_MS=12e4;transport;inputStream;pendingRequests=new Map;pendingInboundRequests=new Map;hooks;hookCallbacks=new Map;nextHookCallbackId=0;sdkMcpServers;sdkMcpTransports;pendingMcpResponses;isSingleTurn;canUseTool;abortController;jsonSchema;initConfig;onElicitation;onAuthExpired;fetchJobToken;diagnostics;controlRequestTimeoutMs;resolveModel;resolveModelTimeoutMs;initResponse;initializationPromise;resultMessage;readLoopPromise;closed=!1;authExpiredFired=!1;protocolHandshakeValidated=!1;firstResultReceived=!1;firstResultReceivedResolve;constructor(e,t,o,r,n,i,a,l,c,p,d,f,m,u,y,g,b){if(this.transport=e,this.isSingleTurn=t,this.canUseTool=o,this.hooks=r??{},this.abortController=n,this.sdkMcpServers=i??new Map,this.sdkMcpTransports=m??new Map,this.pendingMcpResponses=u??new Map,this.jsonSchema=a,this.initConfig=l??{},this.onElicitation=c,this.onAuthExpired=d,this.fetchJobToken=y,this.diagnostics=p,this.controlRequestTimeoutMs=f??6e4,this.resolveModel=g,this.resolveModelTimeoutMs=b??500,this.inputStream=new v(()=>{this.close().catch(()=>{})}),this.readLoopPromise=this.readMessages().catch(S=>{this.inputStream.error(S)}),this.initializationPromise=this.performInitialize(),this.initializationPromise.catch(()=>{}),this.abortController){let S=this.abortController.signal;S.aborted?this.close().catch(()=>{}):S.addEventListener("abort",()=>{this.close().catch(()=>{})},{once:!0})}}[Symbol.asyncIterator](){return this}async next(){return this.inputStream.next()}async return(){return await this.close(),{done:!0,value:void 0}}async throw(e){return await this.inputStream.throw(e),{done:!0,value:void 0}}async readMessages(){let e;try{for await(let t of this.transport.readMessages()){if(this.closed)break;if(U(t)){this.handleControlResponse(t);continue}if(z(t)){this.handleControlRequest(t).catch(r=>{this.diagnostics?.record("[QueryRunner] Error handling control request:",r)});continue}if(J(t)){this.handleControlCancelRequest(t);continue}if(H(t)||$(t)||Q(t)||L(t))continue;if(F(t)){let r=t;this.resultMessage=r,this.firstResultReceived=!0,this.firstResultReceivedResolve?.(),this.firstResultReceivedResolve=void 0,this.inputStream.enqueue(r),this.isSingleTurn&&this.transport.endInput();continue}let o=t;if(o.type==="system"&&o.subtype==="init")try{this.validateProtocolHandshake(o)}catch(r){throw this.inputStream.error(r),r}if(this.onAuthExpired&&!this.authExpiredFired){let r=!1;(o.type==="auth_status"&&o.error||o.type==="assistant"&&o.error==="authentication_failed")&&(r=!0),r&&(this.authExpiredFired=!0,Promise.resolve(this.onAuthExpired()).catch(i=>{this.diagnostics?.record("[QueryRunner] onAuthExpired callback threw:",i)}))}this.inputStream.enqueue(o)}}catch(t){e=t}finally{let t=e instanceof Error?e:new Error("Transport closed");this.firstResultReceivedResolve?.(),this.firstResultReceivedResolve=void 0;for(let[,o]of this.pendingRequests)o.reject(t);this.pendingRequests.clear(),e!==void 0?this.inputStream.error(e):this.inputStream.done()}}async request(e,t){let o=A(),r=j(o,e),n=t?.timeoutMs??this.controlRequestTimeoutMs;return new Promise((i,a)=>{let l=null,c=()=>{l&&(clearTimeout(l),l=null)};this.pendingRequests.set(o,{resolve:p=>{c(),i(p)},reject:p=>{c(),a(p)}}),n>0&&Number.isFinite(n)&&(l=setTimeout(()=>{if(this.pendingRequests.has(o)){this.pendingRequests.delete(o);try{this.transport.write(JSON.stringify({type:"control_cancel_request",request_id:o})+`
|
|
5
5
|
`)}catch(p){this.diagnostics?.record("[QueryRunner] Failed to send control_cancel_request on timeout:",p)}a(new Error(`Control request "${P(r.request)??"unknown"}" timed out after ${n}ms`))}},n));try{this.transport.write(JSON.stringify(r)+`
|
|
6
6
|
`)}catch(p){c(),this.pendingRequests.delete(o),a(p)}})}handleControlResponse(e){let t=e.response,o=t.request_id,r=this.pendingRequests.get(o);if(r)if(this.pendingRequests.delete(o),t.subtype==="success")r.resolve(t.response);else{let n=new Error(t.error);t.code&&(n.code=t.code),r.reject(n),this.processPendingPermissionRequests(t.pending_permission_requests)}}processPendingPermissionRequests(e){if(e)for(let t of e)P(t.request)==="can_use_tool"&&this.handleControlRequest(t).catch(o=>{this.diagnostics?.record("[QueryRunner] Error handling pending permission request:",o)})}async handleControlRequest(e){let t=e.request_id,o=new AbortController;this.pendingInboundRequests.set(t,{abortController:o});try{let r=await this.awaitAbortable(this.processControlRequest(e,o.signal),o.signal);if(!o.signal.aborted){let n=O(t,r);this.transport.write(JSON.stringify(n)+`
|
|
7
7
|
`)}}catch(r){if(!o.signal.aborted){let{error:n,code:i}=oe(r),a=N(t,n,i);this.transport.write(JSON.stringify(a)+`
|
|
8
8
|
`)}}finally{this.pendingInboundRequests.delete(t)}}handleControlCancelRequest(e){let t=this.pendingInboundRequests.get(e.request_id);t&&(t.abortController.abort(),this.pendingInboundRequests.delete(e.request_id))}throwIfAborted(e){if(e.aborted)throw new k}validateProtocolHandshake(e){if(this.protocolHandshakeValidated)return;this.protocolHandshakeValidated=!0;let t=e.protocol_version,o=E;if(!t){this.diagnostics?.record(`[QueryRunner] CLI did not announce protocol_version. This SDK was built for ${o}. Consider upgrading qodercli for the version handshake.`);return}let r=W(t),n=W(o);if(!r||!n){this.diagnostics?.record(`[QueryRunner] Could not parse protocol_version "${t}" or built-in "${o}"; skipping handshake.`);return}if(r.major!==n.major)throw new w(t,o);r.minor<n.minor&&this.diagnostics?.record(`[QueryRunner] CLI protocol_version ${t} is older than this SDK's ${o}. New optional fields may be absent.`)}async awaitAbortable(e,t){if(t.aborted)throw new k;return new Promise((o,r)=>{let n=()=>{r(new k)};t.addEventListener("abort",n,{once:!0}),e.then(i=>{t.removeEventListener("abort",n),o(i)},i=>{t.removeEventListener("abort",n),r(i)})})}async processControlRequest(e,t){this.throwIfAborted(t);let o=e.request,r=P(o),n=o;switch(r){case"fetch_job_token":{if(!this.fetchJobToken)throw new Error("job_token_unavailable: No fetchJobToken callback configured");let i=n.reason??"initial",a=n.session_id,l=e.request_id,c=await this.awaitAbortable(this.fetchJobToken({requestId:l,reason:i,sessionId:a},{signal:t}),t);if(!c?.token)throw new Error("job_token_unavailable: Host did not provide a Job Token");return this.diagnostics?.record(`[QueryRunner] fetch_job_token fulfilled: reason=${i}`),{token:c.token}}case"can_use_tool":case"permission_response":{let i=n.tool_name,a=n.input??n.tool_input??{};if(!this.canUseTool)throw new Error("canUseTool callback is not provided.");let l=n.tool_use_id,c=n.permission_suggestions??n.suggestions,p=n.blocked_path,d=n.decision_reason,f=n.decision_reason_type,m=n.classifier_approvable,u=n.title,y=n.display_name,g=n.description,b=n.agent_id,S=ne(i,n.details,n.plan_path,n.plan_content),R=await this.awaitAbortable(this.canUseTool(i,a,{signal:t,suggestions:c,blockedPath:p,decisionReason:d,decisionReasonType:f,classifierApprovable:m,title:u,displayName:y,description:g,toolUseID:l,agentID:b,exitPlanMode:S}),t);if(R.behavior==="deny"&&!R.message)throw new Error("Permission deny result must include a message.");return R.behavior==="allow"?{...R,updatedInput:R.updatedInput??a,toolUseID:l}:{...R,toolUseID:l}}case"hook_callback":{let i=n.callback_id;if(!i)throw new Error("Hook callback missing callback_id");return this.invokeRegisteredHookCallback(i,n.input,n.tool_use_id,t)}case"mcp_message":{let i=n.server_name,a=n.message,l=this.sdkMcpTransports.get(i);if(!l)throw new Error(`Unknown in-process MCP server: ${i}`);return{mcp_response:await this.awaitAbortable(D(i,a,l,this.pendingMcpResponses),t)}}case"elicitation":case"elicitation_response":return this.onElicitation?await this.awaitAbortable(this.onElicitation({serverName:n.mcp_server_name??"",message:n.message??n.prompt??"",mode:n.mode,url:n.url,elicitationId:n.elicitation_id,requestedSchema:n.requested_schema,title:n.title,displayName:n.display_name,description:n.description},{signal:t}),t):{action:"cancel"};case"get_model_policy":{if(!this.resolveModel)return{type:"get_model_policy"};let i={purpose:n.purpose,sessionId:n.sessionId,turnIndex:n.turnIndex,availableModels:n.models??[]},a=this.resolveModelTimeoutMs,l=Promise.resolve(this.resolveModel(i)),c,p=new Promise((m,u)=>{c=setTimeout(()=>{u(new C(a))},a)}),d;try{d=await this.awaitAbortable(Promise.race([l,p]),t)}finally{clearTimeout(c)}let f={type:"get_model_policy",model:typeof d.model=="string"?d.model:d.model.model,parameters:d.parameters};if(typeof d.model!="string"){let{model:m,...u}=d.model;f.custom_model={...u,style:u.style??"openai"}}return f}default:throw new Error(`Unhandled control request subtype: ${r??"unknown"}`)}}async initialize(){return this.initializationPromise||(this.initializationPromise=this.performInitialize()),this.initializationPromise}async performInitialize(){let e={type:"initialize",modelPolicyProvider:this.resolveModel!==void 0,supportsCatalogReadyInitialize:!0,initializeTimeoutMs:s.INITIALIZE_TIMEOUT_MS},t={};for(let[r,n]of Object.entries(this.hooks)){if(!n||n.length===0)continue;let i=[];for(let a of n){let l=a.hooks.map(c=>{let p=`hook_${this.nextHookCallbackId++}`;return this.hookCallbacks.set(p,c),p});l.length!==0&&i.push({matcher:a.matcher,hookCallbackIds:l,timeout:a.timeout})}i.length>0&&(t[r]=i)}Object.keys(t).length>0&&(e.hooks=t),this.initConfig.systemPrompt!==void 0&&(e.systemPrompt=this.initConfig.systemPrompt),this.initConfig.appendSystemPrompt!==void 0&&(e.appendSystemPrompt=this.initConfig.appendSystemPrompt),this.initConfig.planModeInstructions!==void 0&&(e.planModeInstructions=this.initConfig.planModeInstructions),this.initConfig.agents&&(e.agents=this.initConfig.agents),this.initConfig.promptSuggestions!==void 0&&(e.promptSuggestions=this.initConfig.promptSuggestions),this.initConfig.enableFileCheckpointing!==void 0&&(e.enableFileCheckpointing=this.initConfig.enableFileCheckpointing),this.jsonSchema&&(e.jsonSchema=this.jsonSchema),this.sdkMcpServers.size>0&&(e.sdkMcpServers=Array.from(this.sdkMcpServers.keys()));let o=await this.request(e,{timeoutMs:s.INITIALIZE_TIMEOUT_MS});if(o&&typeof o=="object"){let r=o;this.initResponse=r}if(!this.initResponse)throw new Error("Failed to receive initialization response");return this.initResponse}async invokeRegisteredHookCallback(e,t,o,r){let n=this.hookCallbacks.get(e);if(!n)throw new Error(`Unknown hook callback: ${e}`);return await this.awaitAbortable(n(t,o,{signal:r}),r)??{continue:!0}}async initializationResult(){return this.initialize()}async streamInput(e){(async()=>{try{await this.initialize();let t=0;for await(let o of e){if(this.closed)break;t+=1,this.transport.write(JSON.stringify(o)+`
|
|
9
|
-
`)}t>0&&this.hasBidirectionalNeeds()&&await this.waitForFirstResult(),this.transport.endInput()}catch(t){this.diagnostics?.record("[QueryRunner] streamInput error:",t),this.inputStream.error(t instanceof Error?t:new Error(String(t))),this.transport.endInput()}})().catch(()=>{})}async interrupt(){await this.request({type:"interrupt"})}async setPermissionMode(e){await this.request({type:"set_permission_mode",mode:e==="yolo"?"bypassPermissions":e})}hasBidirectionalNeeds(){return this.sdkMcpTransports.size>0||Object.keys(this.hooks).length>0||this.canUseTool!==void 0||this.onElicitation!==void 0||this.fetchJobToken!==void 0||this.resolveModel!==void 0}waitForFirstResult(){return this.firstResultReceived||this.closed?Promise.resolve():new Promise(e=>{if(this.abortController?.signal.aborted){e();return}this.firstResultReceivedResolve=e,this.abortController?.signal.addEventListener("abort",()=>e(),{once:!0})})}async setModel(e){await this.request({type:"set_model",model:e??""})}async setProxy(e){await this.request({type:"set_proxy",proxy:e||null})}async generateSessionTitle(e,t){return(await this.request({type:"generate_session_title",description:e,...t?.persist!==void 0?{persist:t.persist}:{}}))?.title??null}async addDirectories(e){if(!Array.isArray(e))throw new Error("directories must be an array");return await this.request({type:"add_directories",directories:e})}async applyFlagSettings(e){await this.request({type:"apply_flag_settings",settings:e})}async supportedCommands(){return(await this.initializationResult()).commands??[]}async getAvailableModels(){let e={subtype:"get_models"};return(await this.request(e))?.models??[]}async listByokProviders(){try{return(await this.request({type:"get_byok_config"}))?.providers??null}catch{return null}}async validateByokModel(e){try{let t=await this.request({type:"validate_byok_model",provider:e.provider,model:e.model,api_key:e.api_key,...e.url!==void 0?{url:e.url}:{},...e.style!==void 0?{style:e.style}:{}});return typeof t?.success=="boolean"?t.success:null}catch{return null}}async supportedAgents(){return(await this.initializationResult()).agents??[]}async accountInfo(){try{let t=await this.request({type:"account_info"});if(t?.account)return t.account}catch{}return(await this.initializationResult()).account??{}}async mcpServerStatus(){return(await this.request({type:"mcp_status"}))?.servers??[]}async getContextUsage(){return await this.request({type:"get_context_usage"})}async getUsageInfo(){try{let e=await this.request({type:"get_usage_info"});return se(e?.usage)?e.usage:null}catch{return null}}async reloadPlugins(){return await this.request({type:"reload_plugins"})}async rewindFiles(e,t){let o={type:"rewind_files",user_message_id:e};t?.dryRun&&(o.dry_run=!0);let n=await this.request(o);if(!t?.dryRun&&n?.canRewind===!1)throw new Error(typeof n.error=="string"&&n.error.length>0?n.error:"Unable to rewind files.");return n}async seedReadState(e,t){await this.request({type:"seed_read_state",path:e,mtime:t})}async reconnectMcpServer(e){await this.request({type:"mcp_reconnect",serverName:e})}async toggleMcpServer(e,t){await this.request({type:"mcp_toggle",serverName:e,enabled:t})}async setMcpServers(e){return await this.request({type:"mcp_set_servers",servers:e})??{added:[],removed:[],errors:{}}}async injectMcpToken(e,t){await this.request({type:"mcp_inject_token",serverName:e,token:t})}async mcpAuthenticate(e,t){let o=await this.request({type:"mcp_authenticate",serverName:e,...t!==void 0?{redirectUri:t}:{}}),r=o?.requiresUserAction===!0,n=typeof o?.authUrl=="string"?o.authUrl:void 0;if(r&&n===void 0)throw new Error("mcp_authenticate response set requiresUserAction=true but did not include an authUrl");return r?{authUrl:n,requiresUserAction:!0}:{requiresUserAction:!1}}async mcpSubmitOAuthCallbackUrl(e,t){await this.request({type:"mcp_oauth_callback_url",serverName:e,callbackUrl:t})}async mcpClearAuth(e){await this.request({type:"mcp_clear_auth",serverName:e})}async close(){if(this.closed)return;this.closed=!0;for(let[,t]of this.pendingInboundRequests)t.abortController.abort();this.pendingInboundRequests.clear(),this.hookCallbacks.clear();let e=new Error("SDK closing");for(let[t,o]of this.sdkMcpTransports){try{await o.close()}catch(r){this.diagnostics?.record(`[QueryRunner] error closing SDK MCP transport "${t}":`,r)}K(t,e,this.pendingMcpResponses)}this.transport.close();try{await this.readLoopPromise}catch{}}async[Symbol.asyncDispose](){await this.close()}};function _(s){return typeof s=="object"&&s!==null}function h(s){return typeof s=="number"&&Number.isFinite(s)}function V(s){return _(s)?h(s.total)&&h(s.used)&&h(s.remaining)&&h(s.percentage)&&typeof s.unit=="string":!1}function te(s){return _(s)?h(s.used)&&h(s.cap)&&h(s.remaining)&&h(s.percentage)&&typeof s.available=="boolean"&&typeof s.unit=="string":!1}function se(s){return!_(s)||typeof s.userId!="string"||s.userId.length===0||typeof s.userType!="string"||s.userType.length===0||!h(s.totalUsagePercentage)||typeof s.isHighestTier!="boolean"||!h(s.expiresAt)||!V(s.userQuota)||!V(s.addOnQuota)||typeof s.isQuotaExceeded!="boolean"||typeof s.isPlanQuotaProrated!="boolean"||!te(s.orgResourcePackage)?!1:(s.upgradeUrl===void 0||typeof s.upgradeUrl=="string")&&(!_(s.addOnQuota)||s.addOnQuota.detailUrl===void 0||typeof s.addOnQuota.detailUrl=="string")}function oe(s){let e=s instanceof Error?s.message:String(s),t=typeof s=="object"&&s!==null?s.code:void 0,o=typeof t=="string"&&t.length>0?t:void 0,r=/^([a-z][a-z0-9_]*):\s*(.*)$/.exec(e);if(r){let n=o??r[1];return(!o||o===r[1])&&(e=r[2]||e),{error:e,code:n}}return o?{error:e,code:o}:{error:e}}function ne(s,e,t,o){if(s!==q)return;let r=e!==null&&typeof e=="object"?e:void 0,n=r?.type===q||r?.type==="ExitPlanMode",i=n&&typeof r?.planPath=="string"&&r.planPath.length>0?r.planPath:typeof t=="string"&&t.length>0?t:void 0,a=n&&typeof r?.planContent=="string"&&r.planContent.length>0?r.planContent:typeof o=="string"&&o.length>0?o:void 0;if(!a&&i&&(a=B.readFileSync(i,"utf8").trim()||void 0),!(!i&&!a))return{planPath:i,planContent:a}}function W(s){let e=/^(\d+)\.(\d+)(?:\.\d+)?(?:[-+].*)?$/.exec(s);return e?{major:Number(e[1]),minor:Number(e[2])}:null}var T=class{sendMcpMessage;isClosed=!1;onclose;onerror;onmessage;constructor(e){this.sendMcpMessage=e}async start(){}async send(e){if(this.isClosed)throw new Error("Transport is closed");this.sendMcpMessage(e)}async close(){this.isClosed||(this.isClosed=!0,this.onclose?.())}get closed(){return this.isClosed}};function re(s){let{prompt:e,websocket:t,abortController:o,canUseTool:r,hooks:n,mcpServers:i,jsonSchema:a}=s,l=new Map,c=new Map,p=new Map;if(i)for(let[m,u]of Object.entries(i))if(u.type==="sdk"&&"instance"in u){let y=u.instance;l.set(m,y);let g=new T(b=>{x(m,b,p)});c.set(m,g),y.connect(g).catch(()=>{})}else throw new Error(`Browser SDK only supports SDK MCP servers. Server "${m}" has type "${u.type}".`);let d=new M({url:t.url,headers:t.headers,authMessage:t.authMessage,abortController:o}),f=new I(d,!0,r,n,o,l,a,{},void 0,void 0,void 0,void 0,c,p);return f.streamInput(e),f}export{Z as createSdkMcpServer,re as query,X as tool};
|
|
9
|
+
`)}t>0&&this.hasBidirectionalNeeds()&&await this.waitForFirstResult(),this.transport.endInput()}catch(t){this.diagnostics?.record("[QueryRunner] streamInput error:",t),this.inputStream.error(t instanceof Error?t:new Error(String(t))),this.transport.endInput()}})().catch(()=>{})}async interrupt(){await this.request({type:"interrupt"})}async setPermissionMode(e){await this.request({type:"set_permission_mode",mode:e==="yolo"?"bypassPermissions":e})}hasBidirectionalNeeds(){return this.sdkMcpTransports.size>0||Object.keys(this.hooks).length>0||this.canUseTool!==void 0||this.onElicitation!==void 0||this.fetchJobToken!==void 0||this.resolveModel!==void 0}waitForFirstResult(){return this.firstResultReceived||this.closed?Promise.resolve():new Promise(e=>{if(this.abortController?.signal.aborted){e();return}this.firstResultReceivedResolve=e,this.abortController?.signal.addEventListener("abort",()=>e(),{once:!0})})}async setModel(e){await this.request({type:"set_model",model:e??""})}async setProxy(e){await this.request({type:"set_proxy",proxy:e||null})}async generateSessionTitle(e,t){return(await this.request({type:"generate_session_title",description:e,...t?.persist!==void 0?{persist:t.persist}:{}}))?.title??null}async addDirectories(e){if(!Array.isArray(e))throw new Error("directories must be an array");return await this.request({type:"add_directories",directories:e})}async applyFlagSettings(e){await this.request({type:"apply_flag_settings",settings:e})}async supportedCommands(){return(await this.initializationResult()).commands??[]}async getAvailableModels(){let e={subtype:"get_models"};return(await this.request(e))?.models??[]}async listByokProviders(){try{return(await this.request({type:"get_byok_config"}))?.providers??null}catch{return null}}async validateByokModel(e){try{let t=await this.request({type:"validate_byok_model",provider:e.provider,model:e.model,api_key:e.api_key,...e.url!==void 0?{url:e.url}:{},...e.style!==void 0?{style:e.style}:{}});return typeof t?.success=="boolean"?t.success:null}catch{return null}}async supportedAgents(){return(await this.initializationResult()).agents??[]}async accountInfo(){try{let t=await this.request({type:"account_info"});if(t?.account)return t.account}catch{}return(await this.initializationResult()).account??{}}async mcpServerStatus(){return(await this.request({type:"mcp_status"}))?.servers??[]}async getContextUsage(){return await this.request({type:"get_context_usage"})}async getUsageInfo(){try{let e=await this.request({type:"get_usage_info"});return se(e?.usage)?e.usage:null}catch{return null}}async reloadPlugins(){return await this.request({type:"reload_plugins"})}async listPlugins(){return(await this.request({type:"list_plugins"})).plugins}async rewindFiles(e,t){let o={type:"rewind_files",user_message_id:e};t?.dryRun&&(o.dry_run=!0);let n=await this.request(o);if(!t?.dryRun&&n?.canRewind===!1)throw new Error(typeof n.error=="string"&&n.error.length>0?n.error:"Unable to rewind files.");return n}async seedReadState(e,t){await this.request({type:"seed_read_state",path:e,mtime:t})}async reconnectMcpServer(e){await this.request({type:"mcp_reconnect",serverName:e})}async toggleMcpServer(e,t){await this.request({type:"mcp_toggle",serverName:e,enabled:t})}async setMcpServers(e){return await this.request({type:"mcp_set_servers",servers:e})??{added:[],removed:[],errors:{}}}async injectMcpToken(e,t){await this.request({type:"mcp_inject_token",serverName:e,token:t})}async mcpAuthenticate(e,t){let o=await this.request({type:"mcp_authenticate",serverName:e,...t!==void 0?{redirectUri:t}:{}}),r=o?.requiresUserAction===!0,n=typeof o?.authUrl=="string"?o.authUrl:void 0;if(r&&n===void 0)throw new Error("mcp_authenticate response set requiresUserAction=true but did not include an authUrl");return r?{authUrl:n,requiresUserAction:!0}:{requiresUserAction:!1}}async mcpSubmitOAuthCallbackUrl(e,t){await this.request({type:"mcp_oauth_callback_url",serverName:e,callbackUrl:t})}async mcpClearAuth(e){await this.request({type:"mcp_clear_auth",serverName:e})}async close(){if(this.closed)return;this.closed=!0;for(let[,t]of this.pendingInboundRequests)t.abortController.abort();this.pendingInboundRequests.clear(),this.hookCallbacks.clear();let e=new Error("SDK closing");for(let[t,o]of this.sdkMcpTransports){try{await o.close()}catch(r){this.diagnostics?.record(`[QueryRunner] error closing SDK MCP transport "${t}":`,r)}K(t,e,this.pendingMcpResponses)}this.transport.close();try{await this.readLoopPromise}catch{}}async[Symbol.asyncDispose](){await this.close()}};function _(s){return typeof s=="object"&&s!==null}function h(s){return typeof s=="number"&&Number.isFinite(s)}function V(s){return _(s)?h(s.total)&&h(s.used)&&h(s.remaining)&&h(s.percentage)&&typeof s.unit=="string":!1}function te(s){return _(s)?h(s.used)&&h(s.cap)&&h(s.remaining)&&h(s.percentage)&&typeof s.available=="boolean"&&typeof s.unit=="string":!1}function se(s){return!_(s)||typeof s.userId!="string"||s.userId.length===0||typeof s.userType!="string"||s.userType.length===0||!h(s.totalUsagePercentage)||typeof s.isHighestTier!="boolean"||!h(s.expiresAt)||!V(s.userQuota)||!V(s.addOnQuota)||typeof s.isQuotaExceeded!="boolean"||typeof s.isPlanQuotaProrated!="boolean"||!te(s.orgResourcePackage)?!1:(s.upgradeUrl===void 0||typeof s.upgradeUrl=="string")&&(!_(s.addOnQuota)||s.addOnQuota.detailUrl===void 0||typeof s.addOnQuota.detailUrl=="string")}function oe(s){let e=s instanceof Error?s.message:String(s),t=typeof s=="object"&&s!==null?s.code:void 0,o=typeof t=="string"&&t.length>0?t:void 0,r=/^([a-z][a-z0-9_]*):\s*(.*)$/.exec(e);if(r){let n=o??r[1];return(!o||o===r[1])&&(e=r[2]||e),{error:e,code:n}}return o?{error:e,code:o}:{error:e}}function ne(s,e,t,o){if(s!==q)return;let r=e!==null&&typeof e=="object"?e:void 0,n=r?.type===q||r?.type==="ExitPlanMode",i=n&&typeof r?.planPath=="string"&&r.planPath.length>0?r.planPath:typeof t=="string"&&t.length>0?t:void 0,a=n&&typeof r?.planContent=="string"&&r.planContent.length>0?r.planContent:typeof o=="string"&&o.length>0?o:void 0;if(!a&&i&&(a=B.readFileSync(i,"utf8").trim()||void 0),!(!i&&!a))return{planPath:i,planContent:a}}function W(s){let e=/^(\d+)\.(\d+)(?:\.\d+)?(?:[-+].*)?$/.exec(s);return e?{major:Number(e[1]),minor:Number(e[2])}:null}var T=class{sendMcpMessage;isClosed=!1;onclose;onerror;onmessage;constructor(e){this.sendMcpMessage=e}async start(){}async send(e){if(this.isClosed)throw new Error("Transport is closed");this.sendMcpMessage(e)}async close(){this.isClosed||(this.isClosed=!0,this.onclose?.())}get closed(){return this.isClosed}};function re(s){let{prompt:e,websocket:t,abortController:o,canUseTool:r,hooks:n,mcpServers:i,jsonSchema:a}=s,l=new Map,c=new Map,p=new Map;if(i)for(let[m,u]of Object.entries(i))if(u.type==="sdk"&&"instance"in u){let y=u.instance;l.set(m,y);let g=new T(b=>{x(m,b,p)});c.set(m,g),y.connect(g).catch(()=>{})}else throw new Error(`Browser SDK only supports SDK MCP servers. Server "${m}" has type "${u.type}".`);let d=new M({url:t.url,headers:t.headers,authMessage:t.authMessage,abortController:o}),f=new I(d,!0,r,n,o,l,a,{},void 0,void 0,void 0,void 0,c,p);return f.streamInput(e),f}export{Z as createSdkMcpServer,re as query,X as tool};
|
|
@@ -28,8 +28,10 @@ export declare class WebSocketTransport implements Transport {
|
|
|
28
28
|
private messages;
|
|
29
29
|
private keepAliveTimer?;
|
|
30
30
|
private exitError?;
|
|
31
|
+
private initialized;
|
|
31
32
|
constructor(options: WebSocketTransportOptions);
|
|
32
|
-
|
|
33
|
+
initialize(): Promise<void>;
|
|
34
|
+
private open;
|
|
33
35
|
write(data: string): Promise<void>;
|
|
34
36
|
close(): void;
|
|
35
37
|
isReady(): boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type DefaultRuntimeTransport = 'process' | 'worker';
|
|
2
|
+
/**
|
|
3
|
+
* The default transport baked into the packaged SDK bundle.
|
|
4
|
+
*
|
|
5
|
+
* Development / plain `tsc` builds default to `process`. Release bundles replace
|
|
6
|
+
* `__QODER_DEFAULT_RUNTIME_TRANSPORT__` through esbuild `define`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_RUNTIME_TRANSPORT: DefaultRuntimeTransport;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 4. Writes to stdin as JSON + newline
|
|
10
10
|
* 5. Handles graceful shutdown: close stdin → wait → SIGTERM → wait → SIGKILL
|
|
11
11
|
*/
|
|
12
|
-
import type { Transport } from './transport.js';
|
|
12
|
+
import type { QueryTransportProvider, Transport } from './transport.js';
|
|
13
13
|
import type { StdoutMessage } from '../types/control.js';
|
|
14
14
|
import type { SdkPluginConfig, ToolConfig } from '../types/common.js';
|
|
15
15
|
import type { PermissionMode } from '../types/permissions.js';
|
|
@@ -106,7 +106,8 @@ type ToolsOption = string[] | {
|
|
|
106
106
|
type: 'preset';
|
|
107
107
|
preset: 'qodercli';
|
|
108
108
|
};
|
|
109
|
-
export declare class ProcessTransport implements Transport {
|
|
109
|
+
export declare class ProcessTransport implements Transport, QueryTransportProvider<ProcessTransportOptions> {
|
|
110
|
+
static readonly default: ProcessTransport;
|
|
110
111
|
private process;
|
|
111
112
|
private customHandle;
|
|
112
113
|
private options;
|
|
@@ -114,13 +115,13 @@ export declare class ProcessTransport implements Transport {
|
|
|
114
115
|
private closed;
|
|
115
116
|
private initializePromise;
|
|
116
117
|
private authExpiredFired;
|
|
117
|
-
private authPayloadPath;
|
|
118
118
|
private pendingInput;
|
|
119
|
-
private generatedSettingsDir;
|
|
120
119
|
private diagnostics?;
|
|
121
120
|
private processExitError;
|
|
122
121
|
private stderrTail;
|
|
122
|
+
private launchOptionsBuilder;
|
|
123
123
|
constructor(options?: ProcessTransportOptions);
|
|
124
|
+
create(options: ProcessTransportOptions): Transport;
|
|
124
125
|
/**
|
|
125
126
|
* Build CLI arguments, resolve the executable, spawn the process,
|
|
126
127
|
* and mark the transport as ready.
|
|
@@ -179,33 +180,6 @@ export declare class ProcessTransport implements Transport {
|
|
|
179
180
|
* and is used directly as the command.
|
|
180
181
|
*/
|
|
181
182
|
private resolveExecutable;
|
|
182
|
-
/**
|
|
183
|
-
* Build the environment variables for the child process.
|
|
184
|
-
*/
|
|
185
|
-
private buildEnv;
|
|
186
|
-
/**
|
|
187
|
-
* Build the complete CLI argument array from options.
|
|
188
|
-
*/
|
|
189
|
-
private buildArgs;
|
|
190
|
-
private buildSettingsArg;
|
|
191
|
-
private applyFileCheckpointingSetting;
|
|
192
|
-
private applySettingsCompatibility;
|
|
193
|
-
private toQoderDefaultPermissionMode;
|
|
194
|
-
private applyAdditionalDirectories;
|
|
195
|
-
private buildCliMcpServers;
|
|
196
|
-
private collectMcpPolicyPermissionRules;
|
|
197
|
-
private hasMcpPolicyRules;
|
|
198
|
-
private applyMcpPolicyRules;
|
|
199
|
-
private readSettingsFile;
|
|
200
|
-
private writeGeneratedSettingsFile;
|
|
201
|
-
/**
|
|
202
|
-
* Create a one-shot auth payload file with restrictive permissions.
|
|
203
|
-
* The CLI reads and deletes this file; SDK cleans up as fallback.
|
|
204
|
-
*/
|
|
205
|
-
private createAuthPayloadFile;
|
|
206
|
-
/** Convert InternalAuthOptions to the wire SdkAuthPayload format. */
|
|
207
|
-
private buildAuthPayload;
|
|
208
|
-
private resolveAccessToken;
|
|
209
183
|
/** Best-effort cleanup of the auth payload file and its parent directory. */
|
|
210
184
|
private cleanupAuthPayload;
|
|
211
185
|
/** Fire `onAuthExpired` at most once per session. */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ProcessTransportOptions } from './process-transport.js';
|
|
2
|
+
import type { QueryTransportProvider, Transport } from './transport.js';
|
|
3
|
+
export type QueryTransportCreateOptions = ProcessTransportOptions;
|
|
4
|
+
export type QueryTransportProviderOption = QueryTransportProvider<QueryTransportCreateOptions>;
|
|
5
|
+
export declare function isQueryTransportProvider(value: unknown): value is QueryTransportProviderOption;
|
|
6
|
+
export declare function createQueryTransport(provider: QueryTransportProviderOption, options: QueryTransportCreateOptions): Transport;
|