@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/README.md +108 -8
- package/dist/index.cjs +96 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -4
- package/dist/index.d.ts +50 -4
- package/dist/index.js +96 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
| `
|
|
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
|
-
|
|
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,27 @@ 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
|
+
|
|
554
654
|
### v0.4.0
|
|
555
655
|
|
|
556
656
|
#### 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
|
});
|
|
@@ -925,6 +927,8 @@ var CLAUDE_MODELS = [
|
|
|
925
927
|
];
|
|
926
928
|
var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
927
929
|
backend = "claude";
|
|
930
|
+
/** 工具列表缓存 */
|
|
931
|
+
cachedTools = null;
|
|
928
932
|
/** CLI 命令名或路径 */
|
|
929
933
|
get cliCommand() {
|
|
930
934
|
return this.config?.cliPath ?? "claude";
|
|
@@ -1049,6 +1053,40 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
1049
1053
|
this.processManager.removeEphemeral(sessionId);
|
|
1050
1054
|
}
|
|
1051
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
|
+
}
|
|
1052
1090
|
/**
|
|
1053
1091
|
* 列出 Claude 会话
|
|
1054
1092
|
* @param options 查询选项
|
|
@@ -1177,11 +1215,6 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
1177
1215
|
if (appendPrompt) {
|
|
1178
1216
|
args.push("--append-system-prompt", appendPrompt);
|
|
1179
1217
|
}
|
|
1180
|
-
if (request.allowedTools && request.allowedTools.length > 0) {
|
|
1181
|
-
for (const tool of request.allowedTools) {
|
|
1182
|
-
args.push("--allowedTools", tool);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
1218
|
if (request.disallowedTools && request.disallowedTools.length > 0) {
|
|
1186
1219
|
for (const tool of request.disallowedTools) {
|
|
1187
1220
|
args.push("--disallowedTools", tool);
|
|
@@ -1195,16 +1228,17 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
1195
1228
|
if (skipPerms) {
|
|
1196
1229
|
args.push("--dangerously-skip-permissions");
|
|
1197
1230
|
}
|
|
1198
|
-
if (request.mcpServers) {
|
|
1231
|
+
if (request.mcpServers && Object.keys(request.mcpServers).length > 0) {
|
|
1232
|
+
const mcpConfig = {};
|
|
1199
1233
|
for (const [name, config] of Object.entries(request.mcpServers)) {
|
|
1200
|
-
|
|
1234
|
+
mcpConfig[name] = {
|
|
1201
1235
|
command: config.command,
|
|
1202
1236
|
...config.args ? { args: config.args } : {},
|
|
1203
1237
|
...config.env ? { env: config.env } : {},
|
|
1204
1238
|
...config.url ? { url: config.url } : {}
|
|
1205
1239
|
};
|
|
1206
|
-
args.push("--mcp-server", `${name}=${JSON.stringify(serverSpec)}`);
|
|
1207
1240
|
}
|
|
1241
|
+
args.push("--mcp-config", JSON.stringify({ mcpServers: mcpConfig }));
|
|
1208
1242
|
}
|
|
1209
1243
|
return args;
|
|
1210
1244
|
}
|
|
@@ -1879,7 +1913,7 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
1879
1913
|
async *query(request) {
|
|
1880
1914
|
const adapter = this.getAdapter(request.backend);
|
|
1881
1915
|
const backendName = adapter.backend;
|
|
1882
|
-
const mergedRequest = this.mergeRequest(request, backendName);
|
|
1916
|
+
const mergedRequest = await this.mergeRequest(request, backendName);
|
|
1883
1917
|
const processedPrompt = await this.interceptors.processInput(
|
|
1884
1918
|
mergedRequest.prompt,
|
|
1885
1919
|
{ backend: backendName, sessionId: mergedRequest.resume }
|
|
@@ -1921,10 +1955,10 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
1921
1955
|
* 创建交互式多轮对话会话。
|
|
1922
1956
|
* 自动管理 sessionId 和 resume,对调用方透明。
|
|
1923
1957
|
*/
|
|
1924
|
-
session(options) {
|
|
1958
|
+
async session(options) {
|
|
1925
1959
|
const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
|
|
1926
1960
|
const adapter = this.getAdapter(backend);
|
|
1927
|
-
const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
|
|
1961
|
+
const merged = await this.mergeRequest({ ...options, prompt: "" }, backend);
|
|
1928
1962
|
const { prompt: _, ...baseRequest } = merged;
|
|
1929
1963
|
return new InteractiveSession(
|
|
1930
1964
|
crypto.randomUUID(),
|
|
@@ -1932,6 +1966,16 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
1932
1966
|
baseRequest
|
|
1933
1967
|
);
|
|
1934
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
|
+
}
|
|
1935
1979
|
/**
|
|
1936
1980
|
* 注册拦截器
|
|
1937
1981
|
* @param direction 拦截方向
|
|
@@ -1958,8 +2002,9 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
1958
2002
|
/**
|
|
1959
2003
|
* 检查单个后端的健康状态
|
|
1960
2004
|
* @param backend 后端名称
|
|
2005
|
+
* @param options 检测选项。传入 `{ deep: true }` 时会向模型发送一条测试消息验证可用性。
|
|
1961
2006
|
*/
|
|
1962
|
-
async checkBackend(backend) {
|
|
2007
|
+
async checkBackend(backend, options) {
|
|
1963
2008
|
const adapter = this.getAdapter(backend);
|
|
1964
2009
|
try {
|
|
1965
2010
|
const available = await adapter.isAvailable();
|
|
@@ -1978,6 +2023,25 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
1978
2023
|
if (!compat.supported) {
|
|
1979
2024
|
this.emit("health:degraded", backend, compat.warnings.join("; "));
|
|
1980
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
|
+
}
|
|
1981
2045
|
return result;
|
|
1982
2046
|
} catch (err) {
|
|
1983
2047
|
return {
|
|
@@ -2057,6 +2121,8 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
2057
2121
|
* - timeoutMs: request > defaults
|
|
2058
2122
|
* - maxTurns: request > defaults
|
|
2059
2123
|
* - cwd: request > backendConfig
|
|
2124
|
+
* - disallowedTools: defaults + backendConfig + request 三层叠加去重
|
|
2125
|
+
* - onlyTools: 通过 getTools() 获取全量列表,差集计算转为 disallowedTools(与 disallowedTools 互斥,onlyTools 优先)
|
|
2060
2126
|
* - appendSystemPrompt: **追加模式** — defaults + backendConfig + request 三层拼接(换行分隔)
|
|
2061
2127
|
* 但如果 request.systemPrompt 已设置(替换模式),则忽略所有 appendSystemPrompt
|
|
2062
2128
|
*
|
|
@@ -2064,7 +2130,7 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
2064
2130
|
* @param backend 后端名称
|
|
2065
2131
|
* @returns 合并后的请求
|
|
2066
2132
|
*/
|
|
2067
|
-
mergeRequest(request, backend) {
|
|
2133
|
+
async mergeRequest(request, backend) {
|
|
2068
2134
|
const backendConfig = this.config.backends?.[backend];
|
|
2069
2135
|
const defaults = this.config.defaults;
|
|
2070
2136
|
const appendParts = [];
|
|
@@ -2077,12 +2143,28 @@ var Porygon = class extends import_node_events2.EventEmitter {
|
|
|
2077
2143
|
if (request.appendSystemPrompt) {
|
|
2078
2144
|
appendParts.push(request.appendSystemPrompt);
|
|
2079
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
|
+
}
|
|
2080
2161
|
return {
|
|
2081
2162
|
...request,
|
|
2082
2163
|
model: request.model ?? backendConfig?.model,
|
|
2083
2164
|
timeoutMs: request.timeoutMs ?? defaults?.timeoutMs,
|
|
2084
2165
|
maxTurns: request.maxTurns ?? defaults?.maxTurns,
|
|
2085
2166
|
cwd: request.cwd ?? backendConfig?.cwd,
|
|
2167
|
+
disallowedTools: mergedDisallowedTools,
|
|
2086
2168
|
appendSystemPrompt: request.systemPrompt ? void 0 : appendParts.length > 0 ? appendParts.join("\n") : void 0
|
|
2087
2169
|
};
|
|
2088
2170
|
}
|