@snack-kit/porygon 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,7 @@ const porygon = createPorygon({
54
54
  timeoutMs: 300_000,
55
55
  maxTurns: 50,
56
56
  appendSystemPrompt: "请简洁回答",
57
+ disallowedTools: ["WebSearch"], // 全局禁用的工具
57
58
  },
58
59
  proxy: { url: "http://proxy:8080", noProxy: "localhost" },
59
60
  });
@@ -65,7 +66,7 @@ const porygon = createPorygon({
65
66
  |------|------|------|
66
67
  | `defaultBackend` | `string` | 默认后端名称 |
67
68
  | `backends` | `Record<string, BackendConfig>` | 各后端独立配置 |
68
- | `defaults` | `{ appendSystemPrompt?, timeoutMs?, maxTurns? }` | 全局默认参数 |
69
+ | `defaults` | `{ appendSystemPrompt?, timeoutMs?, maxTurns?, disallowedTools? }` | 全局默认参数 |
69
70
  | `proxy` | `{ url: string, noProxy?: string }` | 全局代理(后端级 proxy 优先) |
70
71
 
71
72
  **BackendConfig 字段:**
@@ -80,6 +81,7 @@ const porygon = createPorygon({
80
81
  | `appendSystemPrompt` | `string` | 追加系统提示词 |
81
82
  | `proxy` | `ProxyConfig` | 后端专属代理 |
82
83
  | `cwd` | `string` | 工作目录 |
84
+ | `disallowedTools` | `string[]` | 禁止使用的工具黑名单(后端级) |
83
85
  | `options` | `Record<string, unknown>` | 透传给后端的额外选项(向后兼容,推荐使用上方的显式字段) |
84
86
 
85
87
  ---
@@ -157,8 +159,8 @@ system → stream_chunk* → assistant(turnComplete) → tool_use* → ... → r
157
159
  | `resume` | `string` | 否 | 恢复会话的 session ID |
158
160
  | `systemPrompt` | `string` | 否 | 系统提示词(替换模式,设置后 appendSystemPrompt 失效) |
159
161
  | `appendSystemPrompt` | `string` | 否 | 系统提示词(追加模式,与 config 中的追加内容合并) |
160
- | `allowedTools` | `string[]` | 否 | 允许的工具白名单 |
161
- | `disallowedTools` | `string[]` | 否 | 禁用的工具黑名单 |
162
+ | `onlyTools` | `string[]` | 否 | 仅允许使用的工具白名单(内部通过 getTools() 差集转为黑名单,与 disallowedTools 互斥,优先级更高) |
163
+ | `disallowedTools` | `string[]` | 否 | 禁止使用的工具黑名单 |
162
164
  | `maxTurns` | `number` | 否 | 最大对话轮次 |
163
165
  | `timeoutMs` | `number` | 否 | 超时毫秒数 |
164
166
  | `cwd` | `string` | 否 | 工作目录 |
@@ -174,6 +176,8 @@ system → stream_chunk* → assistant(turnComplete) → tool_use* → ... → r
174
176
  | `timeoutMs` | request > defaults |
175
177
  | `maxTurns` | request > defaults |
176
178
  | `cwd` | request > backendConfig |
179
+ | `onlyTools` | 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(设置后忽略 disallowedTools) |
180
+ | `disallowedTools` | defaults + backendConfig + request 三层叠加去重 |
177
181
  | `appendSystemPrompt` | defaults + backendConfig + request 三层追加拼接(换行分隔)。若 `systemPrompt` 已设置则忽略全部 append |
178
182
 
179
183
  ---
@@ -193,16 +197,32 @@ system → stream_chunk* → assistant(turnComplete) → tool_use* → ... → r
193
197
 
194
198
  ---
195
199
 
196
- ### `porygon.checkBackend(backend): Promise<HealthCheckResult>`
200
+ ### `porygon.checkBackend(backend, options?): Promise<HealthCheckResult>`
197
201
 
198
- 检查单个后端的健康状态。
202
+ 检查单个后端的健康状态。支持两种模式:
203
+
204
+ - **轻量检测**(默认):仅检查 CLI 是否存在 + 版本兼容性
205
+ - **深度检测**(`deep: true`):额外向模型发送一条测试消息,验证 Token/模型/配额是否真正可用
199
206
 
200
207
  ```ts
208
+ // 轻量检测:仅检查 CLI 存在性和版本
201
209
  const result = await porygon.checkBackend("claude");
202
210
  // { available: true, version: "1.2.0", supported: true }
203
- // { available: false, error: "command not found" }
211
+
212
+ // 深度检测:真实调用模型验证可用性
213
+ const deep = await porygon.checkBackend("claude", { deep: true, model: "haiku" });
214
+ // { available: true, version: "1.2.0", supported: true, modelVerified: true }
215
+ // { available: true, version: "1.2.0", supported: true, modelVerified: false, warnings: ["模型验证失败: ..."] }
204
216
  ```
205
217
 
218
+ **CheckBackendOptions:**
219
+
220
+ | 字段 | 类型 | 默认值 | 说明 |
221
+ |------|------|--------|------|
222
+ | `deep` | `boolean` | `false` | 启用深度检测,向模型发送测试消息 |
223
+ | `model` | `string?` | 后端默认 | 深度检测使用的模型 |
224
+ | `timeoutMs` | `number` | `15000` | 深度检测超时(毫秒) |
225
+
206
226
  **HealthCheckResult:**
207
227
 
208
228
  | 字段 | 类型 | 说明 |
@@ -212,6 +232,7 @@ const result = await porygon.checkBackend("claude");
212
232
  | `supported` | `boolean?` | 版本是否在测试范围内 |
213
233
  | `warnings` | `string[]?` | 兼容性警告 |
214
234
  | `error` | `string?` | 错误信息 |
235
+ | `modelVerified` | `boolean?` | 深度检测时模型是否真正响应 |
215
236
 
216
237
  ---
217
238
 
@@ -350,6 +371,23 @@ const models = await porygon.listModels("claude");
350
371
 
351
372
  ---
352
373
 
374
+ ### `porygon.getTools(force?): Promise<string[]>`
375
+
376
+ 获取当前可用的工具列表。首次调用通过发起最小化对话(`maxTurns: 1`)从 `system` 事件中提取,结果会被缓存,后续调用直接返回缓存。
377
+
378
+ ```ts
379
+ const tools = await porygon.getTools();
380
+ console.log(tools);
381
+ // ["Bash", "Read", "Edit", "Write", "Glob", "Grep", "mcp__xxx__yyy", ...]
382
+
383
+ // 强制刷新缓存
384
+ const fresh = await porygon.getTools(true);
385
+ ```
386
+
387
+ > 仅 Claude 后端支持此能力,其他后端返回空数组。
388
+
389
+ ---
390
+
353
391
  ### `porygon.getCapabilities(backend?): AdapterCapabilities`
354
392
 
355
393
  获取后端能力声明。
@@ -422,7 +460,7 @@ porygon.on("health:degraded", (backend: string, warning: string) => {
422
460
  ```ts
423
461
  // 核心
424
462
  export { Porygon, createPorygon } from "@snack-kit/porygon";
425
- export type { PorygonEvents, HealthCheckResult } from "@snack-kit/porygon";
463
+ export type { PorygonEvents, HealthCheckResult, CheckBackendOptions } from "@snack-kit/porygon";
426
464
 
427
465
  // 类型
428
466
  export type {
@@ -446,6 +484,9 @@ export {
446
484
  // 防护拦截器
447
485
  export { createInputGuard, createOutputGuard } from "@snack-kit/porygon";
448
486
 
487
+ // 交互式会话
488
+ export { InteractiveSession } from "@snack-kit/porygon";
489
+
449
490
  // 适配器(自定义扩展用)
450
491
  export { AbstractAgentAdapter, ClaudeAdapter, OpenCodeAdapter } from "@snack-kit/porygon";
451
492
  ```
@@ -499,13 +540,51 @@ const answer = await porygon.run({
499
540
  });
500
541
  ```
501
542
 
543
+ ### 工具控制
544
+
545
+ ```ts
546
+ // 查询所有可用工具
547
+ const tools = await porygon.getTools();
548
+ console.log(tools); // ["Bash", "Read", "Edit", ...]
549
+
550
+ // 黑名单:禁用危险工具
551
+ await porygon.run({
552
+ prompt: "分析代码",
553
+ disallowedTools: ["Bash", "Edit", "Write"],
554
+ });
555
+
556
+ // 白名单:只允许只读工具(内部自动转为黑名单,getTools 结果有缓存)
557
+ await porygon.run({
558
+ prompt: "阅读代码并回答",
559
+ onlyTools: ["Read", "Grep", "Glob"],
560
+ });
561
+
562
+ // 配置级黑名单:全局 + 后端级 + 请求级三层叠加
563
+ const porygon = createPorygon({
564
+ defaults: { disallowedTools: ["WebSearch"] },
565
+ backends: {
566
+ claude: {
567
+ interactive: false,
568
+ disallowedTools: ["Bash"],
569
+ },
570
+ },
571
+ });
572
+ // 此时 disallowedTools = ["WebSearch", "Bash"],无需每次请求重复传入
573
+ ```
574
+
575
+ ---
576
+
502
577
  ### 健康检查后选择可用后端
503
578
 
504
579
  ```ts
505
- // 检查单个后端
580
+ // 轻量检查:CLI 存在性 + 版本
506
581
  const result = await porygon.checkBackend("claude");
507
582
  if (!result.available) console.error(result.error);
508
583
 
584
+ // 深度检查:验证模型真正可用(Token/配额/模型名)
585
+ const deep = await porygon.checkBackend("claude", { deep: true, model: "haiku" });
586
+ if (deep.modelVerified) console.log("模型验证通过");
587
+
509
588
  // 批量检查所有后端
510
589
  const health = await porygon.healthCheck();
511
590
  const backend = health.claude?.available ? "claude" : "opencode";
@@ -551,6 +630,34 @@ npm run playground # 启动 Playground
551
630
 
552
631
  ## Changelog
553
632
 
633
+ ### v0.5.0
634
+
635
+ #### 新特性
636
+
637
+ - **`porygon.getTools(force?)`** — 获取当前可用的工具列表(包括内置工具和 MCP 插件工具)。首次调用通过最小化对话获取并缓存,后续直接返回缓存,传入 `force: true` 强制刷新
638
+ - **`IAgentAdapter.getTools?(force?)`** — 适配器接口新增可选方法,支持缓存与强制刷新
639
+ - **`onlyTools`(请求参数)** — 仅允许使用的工具白名单,内部通过 `getTools()` 获取全量列表后差集计算转为 `disallowedTools`,工具缓存避免重复对话开销
640
+ - **配置级工具黑名单** — `BackendConfig` 和 `defaults` 新增 `disallowedTools` 字段,支持全局或后端级统一禁用工具,三层叠加去重
641
+ - **`checkBackend(backend, options?)` 深度检测模式** — 新增第二个参数 `CheckBackendOptions`,传入 `{ deep: true }` 时在基础 CLI/版本检查通过后,向模型发送一条极短测试消息(`maxTurns: 1`),验证 Token、模型、配额是否真正可用
642
+ - **`CheckBackendOptions`** — 新增选项类型,支持 `deep`(启用深度检测)、`model`(指定验证模型)、`timeoutMs`(深度检测超时,默认 15s)
643
+ - **`HealthCheckResult.modelVerified`** — 新增字段,深度检测时标识模型是否真正响应
644
+
645
+ #### 破坏性变更
646
+
647
+ - **`PromptRequest.allowedTools` 移除** — Claude CLI 的 `--allowedTools` 实际语义为"免确认"而非"限制范围",已移除避免误用。替代方案:`onlyTools`(白名单,自动转黑名单)或 `disallowedTools`(黑名单)
648
+ - **`porygon.session()` 返回类型变更** — 从 `InteractiveSession` 改为 `Promise<InteractiveSession>`,调用方需 `await`
649
+
650
+ #### Bug Fixes
651
+
652
+ - **Claude adapter 类型定义**: 修复 `ClaudeResultEvent` 缺少 `total_cost_usd` 字段导致的 TS2322 类型错误
653
+
654
+ ### v0.4.0
655
+
656
+ #### Bug Fixes
657
+
658
+ - **Claude adapter**: 修复 `result` 事件中 `inputTokens` 和 `outputTokens` 始终为 0 的问题。Claude CLI 的 token 统计位于 `event.usage.input_tokens` / `event.usage.output_tokens`,而非顶层的 `event.input_tokens` / `event.output_tokens`
659
+ - **Claude adapter**: 修复 `costUsd` 始终为 `undefined` 的问题。Claude CLI 使用 `total_cost_usd` 字段名,而非 `cost_usd`
660
+
554
661
  ### v0.3.0
555
662
 
556
663
  #### Bug Fixes
package/dist/index.cjs CHANGED
@@ -134,6 +134,7 @@ var BackendConfigSchema = import_zod.z.object({
134
134
  apiKey: import_zod.z.string().optional(),
135
135
  interactive: import_zod.z.boolean().optional(),
136
136
  cliPath: import_zod.z.string().optional(),
137
+ disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
137
138
  options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
138
139
  });
139
140
  var PorygonConfigSchema = import_zod.z.object({
@@ -142,7 +143,8 @@ var PorygonConfigSchema = import_zod.z.object({
142
143
  defaults: import_zod.z.object({
143
144
  appendSystemPrompt: import_zod.z.string().optional(),
144
145
  timeoutMs: import_zod.z.number().positive().optional(),
145
- maxTurns: import_zod.z.number().int().positive().optional()
146
+ maxTurns: import_zod.z.number().int().positive().optional(),
147
+ disallowedTools: import_zod.z.array(import_zod.z.string()).optional()
146
148
  }).optional(),
147
149
  proxy: ProxyConfigSchema.optional()
148
150
  });
@@ -859,14 +861,15 @@ function mapClaudeEvent(event, sessionId) {
859
861
  return [];
860
862
  }
861
863
  if (isResultEvent(event)) {
864
+ const usage = event.usage;
862
865
  return [{
863
866
  ...baseFields,
864
867
  type: "result",
865
868
  text: event.result,
866
- costUsd: event.cost_usd,
869
+ costUsd: event.total_cost_usd ?? event.cost_usd,
867
870
  durationMs: event.duration_ms,
868
- inputTokens: event.input_tokens,
869
- outputTokens: event.output_tokens
871
+ inputTokens: usage?.input_tokens ?? event.input_tokens,
872
+ outputTokens: usage?.output_tokens ?? event.output_tokens
870
873
  }];
871
874
  }
872
875
  return [];
@@ -924,6 +927,8 @@ var CLAUDE_MODELS = [
924
927
  ];
925
928
  var ClaudeAdapter = class extends AbstractAgentAdapter {
926
929
  backend = "claude";
930
+ /** 工具列表缓存 */
931
+ cachedTools = null;
927
932
  /** CLI 命令名或路径 */
928
933
  get cliCommand() {
929
934
  return this.config?.cliPath ?? "claude";
@@ -1048,6 +1053,40 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1048
1053
  this.processManager.removeEphemeral(sessionId);
1049
1054
  }
1050
1055
  }
1056
+ /**
1057
+ * 获取当前可用的工具列表。
1058
+ * 首次调用通过发起最小化对话从 system 事件中提取 tools 字段,结果会被缓存。
1059
+ * @param force 是否强制刷新缓存
1060
+ */
1061
+ async getTools(force) {
1062
+ if (!force && this.cachedTools) {
1063
+ return this.cachedTools;
1064
+ }
1065
+ const DEFAULT_TIMEOUT_MS2 = 15e3;
1066
+ let tools = [];
1067
+ for await (const message of this.query({
1068
+ prompt: "hi",
1069
+ maxTurns: 1,
1070
+ timeoutMs: DEFAULT_TIMEOUT_MS2
1071
+ })) {
1072
+ const raw = message.raw;
1073
+ if (message.type === "system" && raw?.tools) {
1074
+ const rawTools = raw.tools;
1075
+ if (Array.isArray(rawTools)) {
1076
+ tools = rawTools.map((t) => {
1077
+ if (typeof t === "string") return t;
1078
+ if (typeof t === "object" && t !== null && "name" in t) {
1079
+ return String(t.name);
1080
+ }
1081
+ return String(t);
1082
+ });
1083
+ break;
1084
+ }
1085
+ }
1086
+ }
1087
+ this.cachedTools = tools;
1088
+ return tools;
1089
+ }
1051
1090
  /**
1052
1091
  * 列出 Claude 会话
1053
1092
  * @param options 查询选项
@@ -1176,11 +1215,6 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1176
1215
  if (appendPrompt) {
1177
1216
  args.push("--append-system-prompt", appendPrompt);
1178
1217
  }
1179
- if (request.allowedTools && request.allowedTools.length > 0) {
1180
- for (const tool of request.allowedTools) {
1181
- args.push("--allowedTools", tool);
1182
- }
1183
- }
1184
1218
  if (request.disallowedTools && request.disallowedTools.length > 0) {
1185
1219
  for (const tool of request.disallowedTools) {
1186
1220
  args.push("--disallowedTools", tool);
@@ -1194,16 +1228,17 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1194
1228
  if (skipPerms) {
1195
1229
  args.push("--dangerously-skip-permissions");
1196
1230
  }
1197
- if (request.mcpServers) {
1231
+ if (request.mcpServers && Object.keys(request.mcpServers).length > 0) {
1232
+ const mcpConfig = {};
1198
1233
  for (const [name, config] of Object.entries(request.mcpServers)) {
1199
- const serverSpec = {
1234
+ mcpConfig[name] = {
1200
1235
  command: config.command,
1201
1236
  ...config.args ? { args: config.args } : {},
1202
1237
  ...config.env ? { env: config.env } : {},
1203
1238
  ...config.url ? { url: config.url } : {}
1204
1239
  };
1205
- args.push("--mcp-server", `${name}=${JSON.stringify(serverSpec)}`);
1206
1240
  }
1241
+ args.push("--mcp-config", JSON.stringify({ mcpServers: mcpConfig }));
1207
1242
  }
1208
1243
  return args;
1209
1244
  }
@@ -1878,7 +1913,7 @@ var Porygon = class extends import_node_events2.EventEmitter {
1878
1913
  async *query(request) {
1879
1914
  const adapter = this.getAdapter(request.backend);
1880
1915
  const backendName = adapter.backend;
1881
- const mergedRequest = this.mergeRequest(request, backendName);
1916
+ const mergedRequest = await this.mergeRequest(request, backendName);
1882
1917
  const processedPrompt = await this.interceptors.processInput(
1883
1918
  mergedRequest.prompt,
1884
1919
  { backend: backendName, sessionId: mergedRequest.resume }
@@ -1920,10 +1955,10 @@ var Porygon = class extends import_node_events2.EventEmitter {
1920
1955
  * 创建交互式多轮对话会话。
1921
1956
  * 自动管理 sessionId 和 resume,对调用方透明。
1922
1957
  */
1923
- session(options) {
1958
+ async session(options) {
1924
1959
  const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
1925
1960
  const adapter = this.getAdapter(backend);
1926
- const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
1961
+ const merged = await this.mergeRequest({ ...options, prompt: "" }, backend);
1927
1962
  const { prompt: _, ...baseRequest } = merged;
1928
1963
  return new InteractiveSession(
1929
1964
  crypto.randomUUID(),
@@ -1931,6 +1966,16 @@ var Porygon = class extends import_node_events2.EventEmitter {
1931
1966
  baseRequest
1932
1967
  );
1933
1968
  }
1969
+ /**
1970
+ * 获取当前可用的工具列表。
1971
+ * 首次调用通过发起最小化对话从 system 事件中提取工具清单,结果会被缓存。
1972
+ * @param force 是否强制刷新缓存
1973
+ */
1974
+ async getTools(force) {
1975
+ const adapter = this.getAdapter();
1976
+ if (!adapter.getTools) return [];
1977
+ return adapter.getTools(force);
1978
+ }
1934
1979
  /**
1935
1980
  * 注册拦截器
1936
1981
  * @param direction 拦截方向
@@ -1957,8 +2002,9 @@ var Porygon = class extends import_node_events2.EventEmitter {
1957
2002
  /**
1958
2003
  * 检查单个后端的健康状态
1959
2004
  * @param backend 后端名称
2005
+ * @param options 检测选项。传入 `{ deep: true }` 时会向模型发送一条测试消息验证可用性。
1960
2006
  */
1961
- async checkBackend(backend) {
2007
+ async checkBackend(backend, options) {
1962
2008
  const adapter = this.getAdapter(backend);
1963
2009
  try {
1964
2010
  const available = await adapter.isAvailable();
@@ -1977,6 +2023,25 @@ var Porygon = class extends import_node_events2.EventEmitter {
1977
2023
  if (!compat.supported) {
1978
2024
  this.emit("health:degraded", backend, compat.warnings.join("; "));
1979
2025
  }
2026
+ if (options?.deep) {
2027
+ try {
2028
+ const response = await this.run({
2029
+ prompt: "Reply with exactly: ok",
2030
+ backend,
2031
+ model: options.model,
2032
+ systemPrompt: "You are a health check probe. Reply with exactly one word: ok",
2033
+ timeoutMs: options.timeoutMs ?? 15e3,
2034
+ maxTurns: 1
2035
+ });
2036
+ result.modelVerified = typeof response === "string" && response.length > 0;
2037
+ } catch (err) {
2038
+ result.modelVerified = false;
2039
+ result.warnings = [
2040
+ ...result.warnings || [],
2041
+ `\u6A21\u578B\u9A8C\u8BC1\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
2042
+ ];
2043
+ }
2044
+ }
1980
2045
  return result;
1981
2046
  } catch (err) {
1982
2047
  return {
@@ -2056,6 +2121,8 @@ var Porygon = class extends import_node_events2.EventEmitter {
2056
2121
  * - timeoutMs: request > defaults
2057
2122
  * - maxTurns: request > defaults
2058
2123
  * - cwd: request > backendConfig
2124
+ * - disallowedTools: defaults + backendConfig + request 三层叠加去重
2125
+ * - onlyTools: 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(与 disallowedTools 互斥,onlyTools 优先)
2059
2126
  * - appendSystemPrompt: **追加模式** — defaults + backendConfig + request 三层拼接(换行分隔)
2060
2127
  * 但如果 request.systemPrompt 已设置(替换模式),则忽略所有 appendSystemPrompt
2061
2128
  *
@@ -2063,7 +2130,7 @@ var Porygon = class extends import_node_events2.EventEmitter {
2063
2130
  * @param backend 后端名称
2064
2131
  * @returns 合并后的请求
2065
2132
  */
2066
- mergeRequest(request, backend) {
2133
+ async mergeRequest(request, backend) {
2067
2134
  const backendConfig = this.config.backends?.[backend];
2068
2135
  const defaults = this.config.defaults;
2069
2136
  const appendParts = [];
@@ -2076,12 +2143,28 @@ var Porygon = class extends import_node_events2.EventEmitter {
2076
2143
  if (request.appendSystemPrompt) {
2077
2144
  appendParts.push(request.appendSystemPrompt);
2078
2145
  }
2146
+ let mergedDisallowedTools;
2147
+ if (request.onlyTools) {
2148
+ const adapter = this.getAdapter(backend);
2149
+ const allTools = adapter.getTools ? await adapter.getTools() : [];
2150
+ const allowSet = new Set(request.onlyTools);
2151
+ const disallowed = allTools.filter((t) => !allowSet.has(t));
2152
+ mergedDisallowedTools = disallowed.length > 0 ? disallowed : void 0;
2153
+ } else {
2154
+ const disallowParts = [
2155
+ ...defaults?.disallowedTools ?? [],
2156
+ ...backendConfig?.disallowedTools ?? [],
2157
+ ...request.disallowedTools ?? []
2158
+ ];
2159
+ mergedDisallowedTools = disallowParts.length > 0 ? [...new Set(disallowParts)] : void 0;
2160
+ }
2079
2161
  return {
2080
2162
  ...request,
2081
2163
  model: request.model ?? backendConfig?.model,
2082
2164
  timeoutMs: request.timeoutMs ?? defaults?.timeoutMs,
2083
2165
  maxTurns: request.maxTurns ?? defaults?.maxTurns,
2084
2166
  cwd: request.cwd ?? backendConfig?.cwd,
2167
+ disallowedTools: mergedDisallowedTools,
2085
2168
  appendSystemPrompt: request.systemPrompt ? void 0 : appendParts.length > 0 ? appendParts.join("\n") : void 0
2086
2169
  };
2087
2170
  }