@snack-kit/porygon 0.4.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/dist/index.d.cts CHANGED
@@ -23,6 +23,8 @@ interface BackendConfig {
23
23
  interactive?: boolean;
24
24
  /** CLI 可执行文件路径(如 Claude CLI 的自定义安装路径) */
25
25
  cliPath?: string;
26
+ /** 禁止使用的工具黑名单(后端级) */
27
+ disallowedTools?: string[];
26
28
  /** 透传给 CLI 的后端特定选项(向后兼容,推荐使用上方的显式字段) */
27
29
  options?: Record<string, unknown>;
28
30
  }
@@ -36,6 +38,8 @@ interface PorygonConfig {
36
38
  appendSystemPrompt?: string;
37
39
  timeoutMs?: number;
38
40
  maxTurns?: number;
41
+ /** 禁止使用的工具黑名单(全局默认) */
42
+ disallowedTools?: string[];
39
43
  };
40
44
  proxy?: ProxyConfig;
41
45
  }
@@ -63,7 +67,13 @@ interface PromptRequest {
63
67
  appendSystemPrompt?: string;
64
68
  model?: string;
65
69
  timeoutMs?: number;
66
- allowedTools?: string[];
70
+ /**
71
+ * 仅允许使用的工具白名单。
72
+ * 运行时通过 getTools() 获取全量工具列表,差集计算后转为 disallowedTools 传递给 CLI。
73
+ * 与 disallowedTools 互斥,同时设置时 onlyTools 优先。
74
+ */
75
+ onlyTools?: string[];
76
+ /** 禁止使用的工具黑名单 */
67
77
  disallowedTools?: string[];
68
78
  mcpServers?: Record<string, McpServerConfig>;
69
79
  maxTurns?: number;
@@ -180,6 +190,8 @@ interface IAgentAdapter {
180
190
  /** 获取可用模型列表 */
181
191
  listModels(): Promise<ModelInfo[]>;
182
192
  abort(sessionId: string): void;
193
+ /** 获取当前可用的工具列表(可选实现,支持缓存) */
194
+ getTools?(force?: boolean): Promise<string[]>;
183
195
  /** 删除会话及其资源(可选实现) */
184
196
  deleteSession?(sessionId: string): Promise<void>;
185
197
  dispose(): Promise<void>;
@@ -250,6 +262,23 @@ interface HealthCheckResult {
250
262
  supported?: boolean;
251
263
  warnings?: string[];
252
264
  error?: string;
265
+ /** deep 模式下,模型是否真正响应 */
266
+ modelVerified?: boolean;
267
+ }
268
+ /**
269
+ * checkBackend 的选项
270
+ */
271
+ interface CheckBackendOptions {
272
+ /**
273
+ * 启用深度检测:在基础 CLI/版本检查通过后,向模型发送一条极短测试消息,
274
+ * 验证 Token/模型/配额是否真正可用。
275
+ * 默认 false(仅做 CLI 存在性 + 版本检查)。
276
+ */
277
+ deep?: boolean;
278
+ /** deep 模式下使用的模型(可选,不传则用后端默认模型) */
279
+ model?: string;
280
+ /** deep 模式超时(毫秒),默认 15000 */
281
+ timeoutMs?: number;
253
282
  }
254
283
  /**
255
284
  * Porygon 事件类型定义
@@ -306,7 +335,13 @@ declare class Porygon extends EventEmitter {
306
335
  * 创建交互式多轮对话会话。
307
336
  * 自动管理 sessionId 和 resume,对调用方透明。
308
337
  */
309
- session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
338
+ session(options?: Omit<PromptRequest, "prompt">): Promise<InteractiveSession>;
339
+ /**
340
+ * 获取当前可用的工具列表。
341
+ * 首次调用通过发起最小化对话从 system 事件中提取工具清单,结果会被缓存。
342
+ * @param force 是否强制刷新缓存
343
+ */
344
+ getTools(force?: boolean): Promise<string[]>;
310
345
  /**
311
346
  * 注册拦截器
312
347
  * @param direction 拦截方向
@@ -327,8 +362,9 @@ declare class Porygon extends EventEmitter {
327
362
  /**
328
363
  * 检查单个后端的健康状态
329
364
  * @param backend 后端名称
365
+ * @param options 检测选项。传入 `{ deep: true }` 时会向模型发送一条测试消息验证可用性。
330
366
  */
331
- checkBackend(backend: string): Promise<HealthCheckResult>;
367
+ checkBackend(backend: string, options?: CheckBackendOptions): Promise<HealthCheckResult>;
332
368
  /**
333
369
  * 对所有已注册后端进行健康检查。
334
370
  * 返回扁平化结构,包含 version/supported/warnings 等字段。
@@ -365,6 +401,8 @@ declare class Porygon extends EventEmitter {
365
401
  * - timeoutMs: request > defaults
366
402
  * - maxTurns: request > defaults
367
403
  * - cwd: request > backendConfig
404
+ * - disallowedTools: defaults + backendConfig + request 三层叠加去重
405
+ * - onlyTools: 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(与 disallowedTools 互斥,onlyTools 优先)
368
406
  * - appendSystemPrompt: **追加模式** — defaults + backendConfig + request 三层拼接(换行分隔)
369
407
  * 但如果 request.systemPrompt 已设置(替换模式),则忽略所有 appendSystemPrompt
370
408
  *
@@ -671,6 +709,8 @@ declare abstract class AbstractAgentAdapter implements IAgentAdapter {
671
709
  */
672
710
  declare class ClaudeAdapter extends AbstractAgentAdapter {
673
711
  readonly backend = "claude";
712
+ /** 工具列表缓存 */
713
+ private cachedTools;
674
714
  /** CLI 命令名或路径 */
675
715
  private get cliCommand();
676
716
  /**
@@ -690,6 +730,12 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
690
730
  * @param request 提示请求参数
691
731
  */
692
732
  query(request: PromptRequest): AsyncGenerator<AgentMessage>;
733
+ /**
734
+ * 获取当前可用的工具列表。
735
+ * 首次调用通过发起最小化对话从 system 事件中提取 tools 字段,结果会被缓存。
736
+ * @param force 是否强制刷新缓存
737
+ */
738
+ getTools(force?: boolean): Promise<string[]>;
693
739
  /**
694
740
  * 列出 Claude 会话
695
741
  * @param options 查询选项
@@ -809,4 +855,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
809
855
  private getConfigPath;
810
856
  }
811
857
 
812
- export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
858
+ export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, type CheckBackendOptions, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface BackendConfig {
23
23
  interactive?: boolean;
24
24
  /** CLI 可执行文件路径(如 Claude CLI 的自定义安装路径) */
25
25
  cliPath?: string;
26
+ /** 禁止使用的工具黑名单(后端级) */
27
+ disallowedTools?: string[];
26
28
  /** 透传给 CLI 的后端特定选项(向后兼容,推荐使用上方的显式字段) */
27
29
  options?: Record<string, unknown>;
28
30
  }
@@ -36,6 +38,8 @@ interface PorygonConfig {
36
38
  appendSystemPrompt?: string;
37
39
  timeoutMs?: number;
38
40
  maxTurns?: number;
41
+ /** 禁止使用的工具黑名单(全局默认) */
42
+ disallowedTools?: string[];
39
43
  };
40
44
  proxy?: ProxyConfig;
41
45
  }
@@ -63,7 +67,13 @@ interface PromptRequest {
63
67
  appendSystemPrompt?: string;
64
68
  model?: string;
65
69
  timeoutMs?: number;
66
- allowedTools?: string[];
70
+ /**
71
+ * 仅允许使用的工具白名单。
72
+ * 运行时通过 getTools() 获取全量工具列表,差集计算后转为 disallowedTools 传递给 CLI。
73
+ * 与 disallowedTools 互斥,同时设置时 onlyTools 优先。
74
+ */
75
+ onlyTools?: string[];
76
+ /** 禁止使用的工具黑名单 */
67
77
  disallowedTools?: string[];
68
78
  mcpServers?: Record<string, McpServerConfig>;
69
79
  maxTurns?: number;
@@ -180,6 +190,8 @@ interface IAgentAdapter {
180
190
  /** 获取可用模型列表 */
181
191
  listModels(): Promise<ModelInfo[]>;
182
192
  abort(sessionId: string): void;
193
+ /** 获取当前可用的工具列表(可选实现,支持缓存) */
194
+ getTools?(force?: boolean): Promise<string[]>;
183
195
  /** 删除会话及其资源(可选实现) */
184
196
  deleteSession?(sessionId: string): Promise<void>;
185
197
  dispose(): Promise<void>;
@@ -250,6 +262,23 @@ interface HealthCheckResult {
250
262
  supported?: boolean;
251
263
  warnings?: string[];
252
264
  error?: string;
265
+ /** deep 模式下,模型是否真正响应 */
266
+ modelVerified?: boolean;
267
+ }
268
+ /**
269
+ * checkBackend 的选项
270
+ */
271
+ interface CheckBackendOptions {
272
+ /**
273
+ * 启用深度检测:在基础 CLI/版本检查通过后,向模型发送一条极短测试消息,
274
+ * 验证 Token/模型/配额是否真正可用。
275
+ * 默认 false(仅做 CLI 存在性 + 版本检查)。
276
+ */
277
+ deep?: boolean;
278
+ /** deep 模式下使用的模型(可选,不传则用后端默认模型) */
279
+ model?: string;
280
+ /** deep 模式超时(毫秒),默认 15000 */
281
+ timeoutMs?: number;
253
282
  }
254
283
  /**
255
284
  * Porygon 事件类型定义
@@ -306,7 +335,13 @@ declare class Porygon extends EventEmitter {
306
335
  * 创建交互式多轮对话会话。
307
336
  * 自动管理 sessionId 和 resume,对调用方透明。
308
337
  */
309
- session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
338
+ session(options?: Omit<PromptRequest, "prompt">): Promise<InteractiveSession>;
339
+ /**
340
+ * 获取当前可用的工具列表。
341
+ * 首次调用通过发起最小化对话从 system 事件中提取工具清单,结果会被缓存。
342
+ * @param force 是否强制刷新缓存
343
+ */
344
+ getTools(force?: boolean): Promise<string[]>;
310
345
  /**
311
346
  * 注册拦截器
312
347
  * @param direction 拦截方向
@@ -327,8 +362,9 @@ declare class Porygon extends EventEmitter {
327
362
  /**
328
363
  * 检查单个后端的健康状态
329
364
  * @param backend 后端名称
365
+ * @param options 检测选项。传入 `{ deep: true }` 时会向模型发送一条测试消息验证可用性。
330
366
  */
331
- checkBackend(backend: string): Promise<HealthCheckResult>;
367
+ checkBackend(backend: string, options?: CheckBackendOptions): Promise<HealthCheckResult>;
332
368
  /**
333
369
  * 对所有已注册后端进行健康检查。
334
370
  * 返回扁平化结构,包含 version/supported/warnings 等字段。
@@ -365,6 +401,8 @@ declare class Porygon extends EventEmitter {
365
401
  * - timeoutMs: request > defaults
366
402
  * - maxTurns: request > defaults
367
403
  * - cwd: request > backendConfig
404
+ * - disallowedTools: defaults + backendConfig + request 三层叠加去重
405
+ * - onlyTools: 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(与 disallowedTools 互斥,onlyTools 优先)
368
406
  * - appendSystemPrompt: **追加模式** — defaults + backendConfig + request 三层拼接(换行分隔)
369
407
  * 但如果 request.systemPrompt 已设置(替换模式),则忽略所有 appendSystemPrompt
370
408
  *
@@ -671,6 +709,8 @@ declare abstract class AbstractAgentAdapter implements IAgentAdapter {
671
709
  */
672
710
  declare class ClaudeAdapter extends AbstractAgentAdapter {
673
711
  readonly backend = "claude";
712
+ /** 工具列表缓存 */
713
+ private cachedTools;
674
714
  /** CLI 命令名或路径 */
675
715
  private get cliCommand();
676
716
  /**
@@ -690,6 +730,12 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
690
730
  * @param request 提示请求参数
691
731
  */
692
732
  query(request: PromptRequest): AsyncGenerator<AgentMessage>;
733
+ /**
734
+ * 获取当前可用的工具列表。
735
+ * 首次调用通过发起最小化对话从 system 事件中提取 tools 字段,结果会被缓存。
736
+ * @param force 是否强制刷新缓存
737
+ */
738
+ getTools(force?: boolean): Promise<string[]>;
693
739
  /**
694
740
  * 列出 Claude 会话
695
741
  * @param options 查询选项
@@ -809,4 +855,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
809
855
  private getConfigPath;
810
856
  }
811
857
 
812
- export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
858
+ export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, type CheckBackendOptions, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
package/dist/index.js CHANGED
@@ -92,6 +92,7 @@ var BackendConfigSchema = z.object({
92
92
  apiKey: z.string().optional(),
93
93
  interactive: z.boolean().optional(),
94
94
  cliPath: z.string().optional(),
95
+ disallowedTools: z.array(z.string()).optional(),
95
96
  options: z.record(z.string(), z.unknown()).optional()
96
97
  });
97
98
  var PorygonConfigSchema = z.object({
@@ -100,7 +101,8 @@ var PorygonConfigSchema = z.object({
100
101
  defaults: z.object({
101
102
  appendSystemPrompt: z.string().optional(),
102
103
  timeoutMs: z.number().positive().optional(),
103
- maxTurns: z.number().int().positive().optional()
104
+ maxTurns: z.number().int().positive().optional(),
105
+ disallowedTools: z.array(z.string()).optional()
104
106
  }).optional(),
105
107
  proxy: ProxyConfigSchema.optional()
106
108
  });
@@ -883,6 +885,8 @@ var CLAUDE_MODELS = [
883
885
  ];
884
886
  var ClaudeAdapter = class extends AbstractAgentAdapter {
885
887
  backend = "claude";
888
+ /** 工具列表缓存 */
889
+ cachedTools = null;
886
890
  /** CLI 命令名或路径 */
887
891
  get cliCommand() {
888
892
  return this.config?.cliPath ?? "claude";
@@ -1007,6 +1011,40 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1007
1011
  this.processManager.removeEphemeral(sessionId);
1008
1012
  }
1009
1013
  }
1014
+ /**
1015
+ * 获取当前可用的工具列表。
1016
+ * 首次调用通过发起最小化对话从 system 事件中提取 tools 字段,结果会被缓存。
1017
+ * @param force 是否强制刷新缓存
1018
+ */
1019
+ async getTools(force) {
1020
+ if (!force && this.cachedTools) {
1021
+ return this.cachedTools;
1022
+ }
1023
+ const DEFAULT_TIMEOUT_MS2 = 15e3;
1024
+ let tools = [];
1025
+ for await (const message of this.query({
1026
+ prompt: "hi",
1027
+ maxTurns: 1,
1028
+ timeoutMs: DEFAULT_TIMEOUT_MS2
1029
+ })) {
1030
+ const raw = message.raw;
1031
+ if (message.type === "system" && raw?.tools) {
1032
+ const rawTools = raw.tools;
1033
+ if (Array.isArray(rawTools)) {
1034
+ tools = rawTools.map((t) => {
1035
+ if (typeof t === "string") return t;
1036
+ if (typeof t === "object" && t !== null && "name" in t) {
1037
+ return String(t.name);
1038
+ }
1039
+ return String(t);
1040
+ });
1041
+ break;
1042
+ }
1043
+ }
1044
+ }
1045
+ this.cachedTools = tools;
1046
+ return tools;
1047
+ }
1010
1048
  /**
1011
1049
  * 列出 Claude 会话
1012
1050
  * @param options 查询选项
@@ -1135,11 +1173,6 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1135
1173
  if (appendPrompt) {
1136
1174
  args.push("--append-system-prompt", appendPrompt);
1137
1175
  }
1138
- if (request.allowedTools && request.allowedTools.length > 0) {
1139
- for (const tool of request.allowedTools) {
1140
- args.push("--allowedTools", tool);
1141
- }
1142
- }
1143
1176
  if (request.disallowedTools && request.disallowedTools.length > 0) {
1144
1177
  for (const tool of request.disallowedTools) {
1145
1178
  args.push("--disallowedTools", tool);
@@ -1153,16 +1186,17 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1153
1186
  if (skipPerms) {
1154
1187
  args.push("--dangerously-skip-permissions");
1155
1188
  }
1156
- if (request.mcpServers) {
1189
+ if (request.mcpServers && Object.keys(request.mcpServers).length > 0) {
1190
+ const mcpConfig = {};
1157
1191
  for (const [name, config] of Object.entries(request.mcpServers)) {
1158
- const serverSpec = {
1192
+ mcpConfig[name] = {
1159
1193
  command: config.command,
1160
1194
  ...config.args ? { args: config.args } : {},
1161
1195
  ...config.env ? { env: config.env } : {},
1162
1196
  ...config.url ? { url: config.url } : {}
1163
1197
  };
1164
- args.push("--mcp-server", `${name}=${JSON.stringify(serverSpec)}`);
1165
1198
  }
1199
+ args.push("--mcp-config", JSON.stringify({ mcpServers: mcpConfig }));
1166
1200
  }
1167
1201
  return args;
1168
1202
  }
@@ -1837,7 +1871,7 @@ var Porygon = class extends EventEmitter2 {
1837
1871
  async *query(request) {
1838
1872
  const adapter = this.getAdapter(request.backend);
1839
1873
  const backendName = adapter.backend;
1840
- const mergedRequest = this.mergeRequest(request, backendName);
1874
+ const mergedRequest = await this.mergeRequest(request, backendName);
1841
1875
  const processedPrompt = await this.interceptors.processInput(
1842
1876
  mergedRequest.prompt,
1843
1877
  { backend: backendName, sessionId: mergedRequest.resume }
@@ -1879,10 +1913,10 @@ var Porygon = class extends EventEmitter2 {
1879
1913
  * 创建交互式多轮对话会话。
1880
1914
  * 自动管理 sessionId 和 resume,对调用方透明。
1881
1915
  */
1882
- session(options) {
1916
+ async session(options) {
1883
1917
  const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
1884
1918
  const adapter = this.getAdapter(backend);
1885
- const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
1919
+ const merged = await this.mergeRequest({ ...options, prompt: "" }, backend);
1886
1920
  const { prompt: _, ...baseRequest } = merged;
1887
1921
  return new InteractiveSession(
1888
1922
  crypto.randomUUID(),
@@ -1890,6 +1924,16 @@ var Porygon = class extends EventEmitter2 {
1890
1924
  baseRequest
1891
1925
  );
1892
1926
  }
1927
+ /**
1928
+ * 获取当前可用的工具列表。
1929
+ * 首次调用通过发起最小化对话从 system 事件中提取工具清单,结果会被缓存。
1930
+ * @param force 是否强制刷新缓存
1931
+ */
1932
+ async getTools(force) {
1933
+ const adapter = this.getAdapter();
1934
+ if (!adapter.getTools) return [];
1935
+ return adapter.getTools(force);
1936
+ }
1893
1937
  /**
1894
1938
  * 注册拦截器
1895
1939
  * @param direction 拦截方向
@@ -1916,8 +1960,9 @@ var Porygon = class extends EventEmitter2 {
1916
1960
  /**
1917
1961
  * 检查单个后端的健康状态
1918
1962
  * @param backend 后端名称
1963
+ * @param options 检测选项。传入 `{ deep: true }` 时会向模型发送一条测试消息验证可用性。
1919
1964
  */
1920
- async checkBackend(backend) {
1965
+ async checkBackend(backend, options) {
1921
1966
  const adapter = this.getAdapter(backend);
1922
1967
  try {
1923
1968
  const available = await adapter.isAvailable();
@@ -1936,6 +1981,25 @@ var Porygon = class extends EventEmitter2 {
1936
1981
  if (!compat.supported) {
1937
1982
  this.emit("health:degraded", backend, compat.warnings.join("; "));
1938
1983
  }
1984
+ if (options?.deep) {
1985
+ try {
1986
+ const response = await this.run({
1987
+ prompt: "Reply with exactly: ok",
1988
+ backend,
1989
+ model: options.model,
1990
+ systemPrompt: "You are a health check probe. Reply with exactly one word: ok",
1991
+ timeoutMs: options.timeoutMs ?? 15e3,
1992
+ maxTurns: 1
1993
+ });
1994
+ result.modelVerified = typeof response === "string" && response.length > 0;
1995
+ } catch (err) {
1996
+ result.modelVerified = false;
1997
+ result.warnings = [
1998
+ ...result.warnings || [],
1999
+ `\u6A21\u578B\u9A8C\u8BC1\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
2000
+ ];
2001
+ }
2002
+ }
1939
2003
  return result;
1940
2004
  } catch (err) {
1941
2005
  return {
@@ -2015,6 +2079,8 @@ var Porygon = class extends EventEmitter2 {
2015
2079
  * - timeoutMs: request > defaults
2016
2080
  * - maxTurns: request > defaults
2017
2081
  * - cwd: request > backendConfig
2082
+ * - disallowedTools: defaults + backendConfig + request 三层叠加去重
2083
+ * - onlyTools: 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(与 disallowedTools 互斥,onlyTools 优先)
2018
2084
  * - appendSystemPrompt: **追加模式** — defaults + backendConfig + request 三层拼接(换行分隔)
2019
2085
  * 但如果 request.systemPrompt 已设置(替换模式),则忽略所有 appendSystemPrompt
2020
2086
  *
@@ -2022,7 +2088,7 @@ var Porygon = class extends EventEmitter2 {
2022
2088
  * @param backend 后端名称
2023
2089
  * @returns 合并后的请求
2024
2090
  */
2025
- mergeRequest(request, backend) {
2091
+ async mergeRequest(request, backend) {
2026
2092
  const backendConfig = this.config.backends?.[backend];
2027
2093
  const defaults = this.config.defaults;
2028
2094
  const appendParts = [];
@@ -2035,12 +2101,28 @@ var Porygon = class extends EventEmitter2 {
2035
2101
  if (request.appendSystemPrompt) {
2036
2102
  appendParts.push(request.appendSystemPrompt);
2037
2103
  }
2104
+ let mergedDisallowedTools;
2105
+ if (request.onlyTools) {
2106
+ const adapter = this.getAdapter(backend);
2107
+ const allTools = adapter.getTools ? await adapter.getTools() : [];
2108
+ const allowSet = new Set(request.onlyTools);
2109
+ const disallowed = allTools.filter((t) => !allowSet.has(t));
2110
+ mergedDisallowedTools = disallowed.length > 0 ? disallowed : void 0;
2111
+ } else {
2112
+ const disallowParts = [
2113
+ ...defaults?.disallowedTools ?? [],
2114
+ ...backendConfig?.disallowedTools ?? [],
2115
+ ...request.disallowedTools ?? []
2116
+ ];
2117
+ mergedDisallowedTools = disallowParts.length > 0 ? [...new Set(disallowParts)] : void 0;
2118
+ }
2038
2119
  return {
2039
2120
  ...request,
2040
2121
  model: request.model ?? backendConfig?.model,
2041
2122
  timeoutMs: request.timeoutMs ?? defaults?.timeoutMs,
2042
2123
  maxTurns: request.maxTurns ?? defaults?.maxTurns,
2043
2124
  cwd: request.cwd ?? backendConfig?.cwd,
2125
+ disallowedTools: mergedDisallowedTools,
2044
2126
  appendSystemPrompt: request.systemPrompt ? void 0 : appendParts.length > 0 ? appendParts.join("\n") : void 0
2045
2127
  };
2046
2128
  }