evolcore 0.0.25 → 0.0.26
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/CHANGELOG.md +607 -565
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +160 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +40 -29
- package/bin/install-codex-managed-hooks.mjs +582 -555
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +50 -12
- package/dist/agents/codex/app-server-client.js +13 -5
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +121 -38
- package/dist/agents/ecagent-runner.js +4 -9
- package/dist/agents/gemini-runner.js +2 -7
- package/dist/agents/runner-types.js +25 -17
- package/dist/aun/aid/agentmd.js +81 -8
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/menu-client.js +74 -0
- package/dist/aun/msg/group.js +1 -1
- package/dist/aun/msg/managed-operation.js +1 -1
- package/dist/aun/msg/p2p.js +6 -1
- package/dist/aun/msg/upload.js +30 -22
- package/dist/channels/aun.js +329 -94
- package/dist/cli/agent-command.js +105 -84
- package/dist/cli/agent-create.js +112 -10
- package/dist/cli/agent.js +16 -5
- package/dist/cli/aun-commands.js +297 -296
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +47 -28
- package/dist/cli/contact.js +70 -13
- package/dist/cli/ctl-command.js +22 -22
- package/dist/cli/daemon-commands.js +105 -40
- package/dist/cli/diag.js +31 -0
- package/dist/cli/fs-command.js +67 -67
- package/dist/cli/handoff-command.js +41 -9
- package/dist/cli/index.js +83 -77
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +46 -31
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +29 -29
- package/dist/cli/response.js +30 -30
- package/dist/cli/restart-monitor.js +87 -40
- package/dist/cli/stats.js +51 -51
- package/dist/cli/trigger-command.js +56 -56
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-operation-service.js +145 -0
- package/dist/config/contact-operation-service.js +96 -7
- package/dist/config/resolved-config-op.js +27 -7
- package/dist/core/agent-application-service.js +2 -1
- package/dist/core/auth/authorization-audit.js +20 -12
- package/dist/core/auth/operation-authorizer.js +25 -4
- package/dist/core/auth/operation-catalog.js +35 -4
- package/dist/core/auth/remote-access-store.js +40 -1
- package/dist/core/bootstrap-service.js +1 -1
- package/dist/core/command/command-handler.js +83 -26
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/menu-catalog.js +12 -3
- package/dist/core/command/menu-handler.js +47 -26
- package/dist/core/command/menu-protocol.js +5 -0
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +86 -21
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/message/im-renderer.js +72 -21
- package/dist/core/message/message-bridge.js +69 -9
- package/dist/core/message/message-queue.js +26 -0
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +47 -28
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/ec-command-parser.js +51 -12
- package/dist/core/permission/sandbox-runtime.js +36 -14
- package/dist/core/permission/tool-policy.js +2 -1
- package/dist/index.js +280 -163
- package/dist/ipc.js +54 -2
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/price-resolver.js +28 -10
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +21 -0
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +3 -1
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/npm-ops.js +176 -35
- package/dist/utils/welcome.js +22 -22
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +66 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +56 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +364 -364
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +71 -71
- package/kits/docs/evolcore/agent-create.md +170 -165
- package/kits/docs/evolcore/agent.md +66 -66
- package/kits/docs/evolcore/aid.md +60 -60
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
- package/kits/docs/evolcore/config.md +202 -195
- package/kits/docs/evolcore/contact.md +75 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +243 -243
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +152 -152
- package/kits/docs/evolcore/menu-client.md +42 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +166 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +50 -50
- package/kits/docs/evolcore/trigger.md +575 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +269 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +37 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +190 -190
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +162 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +36 -36
- package/kits/rules/04-relation.md +75 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +63 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +427 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +201 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +92 -92
- package/kits/templates/roles/member.json +96 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +85 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +31 -29
- package/kits/templates/system-fragments/channel.md +64 -64
- package/kits/templates/system-fragments/commands.md +28 -28
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +100 -98
|
@@ -3,11 +3,21 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { appendRequestPath, appendV1RequestPath } from '../../agents/request-identity.js';
|
|
5
5
|
import { atomicReadJson, atomicWriteJson } from '../../utils/atomic-write.js';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { parseModelId } from './context-window-suffix.js';
|
|
7
|
+
/** 单页请求的超时(含响应体读取)。多页目录里每页各自享有这个预算。 */
|
|
8
|
+
export const CATALOG_PAGE_TIMEOUT_MS = 4_000;
|
|
9
|
+
/** 整条路由(含全部分页)的总期限,兜住慢网关无限翻页。 */
|
|
10
|
+
export const CATALOG_ROUTE_TIMEOUT_MS = 15_000;
|
|
11
|
+
/**
|
|
12
|
+
* 持久化快照的格式版本。**目录规范化或能力过滤规则变化时必须递增**,
|
|
13
|
+
* 否则旧规则写下的持久化快照与新快照无法区分,会继续被服务。
|
|
14
|
+
* v2:保留上下文窗口后缀(`claude-opus-5[1m]` 不再被折叠成基础 ID)。
|
|
15
|
+
* v3:保留 Codex 上游声明的 max effort,重新获取旧快照中过滤掉的能力。
|
|
16
|
+
*/
|
|
17
|
+
export const CATALOG_SNAPSHOT_VERSION = 3;
|
|
8
18
|
export const TRANSPORT_EFFORTS = {
|
|
9
19
|
claude: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
10
|
-
codex: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'],
|
|
20
|
+
codex: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
11
21
|
ecagent: ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
12
22
|
gemini: [],
|
|
13
23
|
};
|
|
@@ -22,7 +32,7 @@ export const catalogHash = (value) => createHash('sha256').update(stable(value))
|
|
|
22
32
|
export function catalogContextKey(context) {
|
|
23
33
|
const headers = Object.fromEntries(Object.entries(context.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
|
|
24
34
|
return catalogHash({ scope: context.scope, baseagent: context.baseagent, provider: context.provider,
|
|
25
|
-
baseUrl: context.baseUrl?.replace(/\/+$/, ''), apiKey: context.apiKey,
|
|
35
|
+
baseUrl: context.baseUrl?.replace(/\/+$/, ''), apiKey: context.apiKey, authType: context.authType,
|
|
26
36
|
headers, queryParams: context.queryParams ?? {}, configUnavailable: context.configUnavailable });
|
|
27
37
|
}
|
|
28
38
|
const strings = (value) => Array.isArray(value)
|
|
@@ -40,37 +50,45 @@ export function parseModelList(json, _url) {
|
|
|
40
50
|
const out = new Map();
|
|
41
51
|
const items = rows(json);
|
|
42
52
|
for (const item of items) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
// 单个畸形条目只跳过自己:一条读不懂的记录不该让整份目录退化到 builtin。
|
|
54
|
+
try {
|
|
55
|
+
const rawId = typeof item === 'string' ? item : item?.id ?? item?.slug ?? item?.name ?? item?.model;
|
|
56
|
+
if (typeof rawId !== 'string' || !rawId.trim())
|
|
57
|
+
continue;
|
|
58
|
+
// 上下文窗口后缀 [1m]/[200k] 是模型身份的一部分:网关把不同窗口作为独立条目
|
|
59
|
+
// 下发,两个变体必须各自成为一条可选条目。仅规范化后缀大小写以便去重。
|
|
60
|
+
const parsed = parseModelId(rawId.trim());
|
|
61
|
+
const id = `${parsed.base}${parsed.suffix ?? ''}`;
|
|
62
|
+
if (out.has(id))
|
|
63
|
+
continue;
|
|
64
|
+
const rawLevels = item?.reasoningLevels ?? item?.supportedReasoningEfforts ?? item?.supported_reasoning_levels;
|
|
65
|
+
const levels = Array.isArray(rawLevels) ? strings(rawLevels.map(v => typeof v === 'string' ? v : v?.reasoningEffort ?? v?.effort)) : undefined;
|
|
66
|
+
const owner = item?.owned_by ?? item?.owner ?? item?.provider;
|
|
67
|
+
out.set(id, {
|
|
68
|
+
id, verification: 'listed',
|
|
69
|
+
...(typeof owner === 'string' ? { owned_by: owner } : {}),
|
|
70
|
+
...(typeof item?.created === 'number' ? { created: item.created } : {}),
|
|
71
|
+
...(typeof (item?.displayName ?? item?.display_name) === 'string' ? { displayName: item.displayName ?? item.display_name } : {}),
|
|
72
|
+
...(levels ? { reasoningLevels: levels } : {}),
|
|
73
|
+
...(typeof (item?.defaultReasoningEffort ?? item?.default_reasoning_level) === 'string' ? { defaultReasoningLevel: item.defaultReasoningEffort ?? item.default_reasoning_level } : {}),
|
|
74
|
+
...(strings(item?.protocols ?? item?.supported_protocols) ? { protocols: strings(item.protocols ?? item.supported_protocols) } : {}),
|
|
75
|
+
...(typeof item?.capabilities?.tool_calling === 'boolean' ? { supportsTools: item.capabilities.tool_calling } : {}),
|
|
76
|
+
// 网关显式给出的 context_window 优先;缺失时用后缀声明的窗口兜底。
|
|
77
|
+
...(typeof (item?.context_window ?? item?.contextWindow) === 'number' ? { contextWindow: item.context_window ?? item.contextWindow }
|
|
78
|
+
: parsed.contextWindow !== undefined ? { contextWindow: parsed.contextWindow } : {}),
|
|
79
|
+
...(typeof item?.max_output_tokens === 'number' ? { maxOutputTokens: item.max_output_tokens } : {}),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch { /* Skip the offending entry; the rest of the page stays usable. */ }
|
|
66
83
|
}
|
|
84
|
+
// 全部条目都无法解析仍视为不可信响应:避免把一个格式不符的 200 当成空目录。
|
|
67
85
|
if (items.length && !out.size)
|
|
68
86
|
throw new Error('Model list contains no valid IDs');
|
|
69
87
|
return [...out.values()];
|
|
70
88
|
}
|
|
71
89
|
const BUILTIN_IDS = {
|
|
72
90
|
codex: ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.2'],
|
|
73
|
-
claude: ['claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'deepseek-v4-pro', 'deepseek-v4-flash', 'kimi-k2.6', 'kimi-k2.5', 'glm-5.1', 'glm-5', 'glm-4.7', 'MiniMax-M2.7'],
|
|
91
|
+
claude: ['claude-opus-5', 'claude-sonnet-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'deepseek-v4-pro', 'deepseek-v4-flash', 'kimi-k2.6', 'kimi-k2.5', 'glm-5.1', 'glm-5', 'glm-4.7', 'MiniMax-M2.7'],
|
|
74
92
|
ecagent: ['claude-sonnet-4-6'],
|
|
75
93
|
gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite'],
|
|
76
94
|
};
|
|
@@ -89,9 +107,14 @@ export function deriveAliases(models, baseagent) {
|
|
|
89
107
|
if (!match)
|
|
90
108
|
continue;
|
|
91
109
|
const [, family, major, minor = '0'] = match;
|
|
110
|
+
// 同版本的多个窗口变体:取窗口最大的一条,别名才不会随网关下发顺序漂移。
|
|
111
|
+
const window = parseModelId(id).contextWindow ?? 0;
|
|
92
112
|
const current = best[family];
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
const better = !current || +major > current.major
|
|
114
|
+
|| (+major === current.major && +minor > current.minor)
|
|
115
|
+
|| (+major === current.major && +minor === current.minor && window > current.window);
|
|
116
|
+
if (better)
|
|
117
|
+
best[family] = { id, major: +major, minor: +minor, window };
|
|
95
118
|
}
|
|
96
119
|
return Object.fromEntries(Object.entries(best).map(([family, info]) => [family, info.id]));
|
|
97
120
|
}
|
|
@@ -133,12 +156,17 @@ async function gatewayModels(context, fetcher) {
|
|
|
133
156
|
const models = [];
|
|
134
157
|
const seen = new Set();
|
|
135
158
|
let next = url;
|
|
136
|
-
//
|
|
137
|
-
|
|
159
|
+
// 每页各自计时,避免多页目录被一个覆盖全程的超时提前掐断;
|
|
160
|
+
// 同时用总期限兜住整条路由,防止慢网关无限翻页。
|
|
161
|
+
const deadline = Date.now() + CATALOG_ROUTE_TIMEOUT_MS;
|
|
138
162
|
for (let page = 0; page < 100; page++) {
|
|
139
163
|
if (seen.has(next))
|
|
140
164
|
throw new GatewayError();
|
|
141
165
|
seen.add(next);
|
|
166
|
+
const remaining = deadline - Date.now();
|
|
167
|
+
if (remaining <= 0)
|
|
168
|
+
throw new GatewayError();
|
|
169
|
+
const signal = AbortSignal.timeout(Math.min(CATALOG_PAGE_TIMEOUT_MS, remaining));
|
|
142
170
|
const headers = new Headers(context.headers);
|
|
143
171
|
if (context.apiKey && !headers.has('authorization')) {
|
|
144
172
|
headers.set('authorization', `Bearer ${context.apiKey}`);
|
|
@@ -176,7 +204,7 @@ async function gatewayModels(context, fetcher) {
|
|
|
176
204
|
/** One instance per daemon. Offline clients use the same persistence format. */
|
|
177
205
|
export class ModelCatalogService {
|
|
178
206
|
options;
|
|
179
|
-
|
|
207
|
+
latest = new Map();
|
|
180
208
|
successes = new Map();
|
|
181
209
|
inflight = new Map();
|
|
182
210
|
constructor(options = {}) {
|
|
@@ -192,41 +220,43 @@ export class ModelCatalogService {
|
|
|
192
220
|
try {
|
|
193
221
|
const file = this.file(context);
|
|
194
222
|
const saved = file ? atomicReadJson(file) : undefined;
|
|
195
|
-
|
|
223
|
+
// 版本不匹配的快照按冷启动处理:其 ID 可能出自已废弃的规范化规则。
|
|
224
|
+
if (saved?.version === CATALOG_SNAPSHOT_VERSION && saved.snapshot && Array.isArray(saved.snapshot.models) && ['gateway', 'native'].includes(saved.snapshot.source))
|
|
196
225
|
snapshot = saved.snapshot;
|
|
197
226
|
}
|
|
198
227
|
catch { /* A missing/corrupt cache is a cold load. */ }
|
|
199
228
|
if (snapshot)
|
|
200
229
|
this.successes.set(key, snapshot);
|
|
201
230
|
}
|
|
202
|
-
return snapshot
|
|
231
|
+
return snapshot;
|
|
203
232
|
}
|
|
204
233
|
peek(context) {
|
|
205
|
-
return structuredClone(this.
|
|
234
|
+
return structuredClone(this.latest.get(this.key(context)) ?? this.previous(context) ?? this.make(context, 'builtin', 'fallback', this.legacyBuiltins(context), [{ code: 'BUILTIN', message: '内置候选,未经当前连接验证' }]));
|
|
206
235
|
}
|
|
207
236
|
async get(context, refresh = false) {
|
|
208
237
|
const key = this.key(context);
|
|
209
|
-
const
|
|
210
|
-
if (!refresh && cached && cached.retryAt > this.now())
|
|
211
|
-
return structuredClone(cached.snapshot);
|
|
212
|
-
const previous = this.previous(context);
|
|
213
|
-
if (!refresh && previous && this.now() - Date.parse(previous.fetchedAt) < CATALOG_TTL_MS && !cached) {
|
|
214
|
-
this.cache.set(key, { snapshot: previous, retryAt: Date.parse(previous.fetchedAt) + CATALOG_TTL_MS });
|
|
215
|
-
return structuredClone(previous);
|
|
216
|
-
}
|
|
238
|
+
const previous = this.latest.get(key) ?? this.previous(context);
|
|
217
239
|
let pending = this.inflight.get(key);
|
|
218
240
|
if (!pending) {
|
|
219
241
|
pending = this.load(context, previous).then(snapshot => {
|
|
220
|
-
|
|
242
|
+
// Keep any usable first result (including builtin/native fallback), but
|
|
243
|
+
// never let a failed refresh replace the last usable snapshot.
|
|
244
|
+
if (snapshot.freshness !== 'stale')
|
|
245
|
+
this.latest.set(key, snapshot);
|
|
221
246
|
return snapshot;
|
|
222
247
|
}).finally(() => this.inflight.delete(key));
|
|
223
248
|
this.inflight.set(key, pending);
|
|
224
249
|
}
|
|
225
|
-
if (!refresh && previous)
|
|
226
|
-
|
|
250
|
+
if (!refresh && previous) {
|
|
251
|
+
// Serve the last successful catalog immediately on every ordinary read,
|
|
252
|
+
// while each read also asks the shared background job to refresh it.
|
|
253
|
+
// Explicit refresh callers still await that same in-flight request.
|
|
254
|
+
void pending.catch(() => undefined);
|
|
255
|
+
return structuredClone(previous);
|
|
256
|
+
}
|
|
227
257
|
return structuredClone(await pending);
|
|
228
258
|
}
|
|
229
|
-
clear() { this.
|
|
259
|
+
clear() { this.latest.clear(); this.successes.clear(); }
|
|
230
260
|
make(context, source, freshness, models, diagnostics) {
|
|
231
261
|
const entries = adapt(models, context.baseagent);
|
|
232
262
|
const fetchedAt = new Date(this.now()).toISOString();
|
|
@@ -241,7 +271,7 @@ export class ModelCatalogService {
|
|
|
241
271
|
const file = this.file(context);
|
|
242
272
|
if (file) {
|
|
243
273
|
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
244
|
-
atomicWriteJson(file, { version:
|
|
274
|
+
atomicWriteJson(file, { version: CATALOG_SNAPSHOT_VERSION, snapshot });
|
|
245
275
|
}
|
|
246
276
|
}
|
|
247
277
|
catch { /* Persistence failure does not discard a live result. */ }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code SDK 的上下文窗口后缀(如 `claude-opus-5[1m]`)。
|
|
3
|
+
*
|
|
4
|
+
* 网关会把同一模型的不同窗口作为独立条目下发,所以后缀是**模型身份的一部分**,
|
|
5
|
+
* 目录与选择界面必须原样保留——两个变体各成一条可选条目。
|
|
6
|
+
* 只有按基础 ID 归档的场景(查价、计费、能力参数表)才剥离后缀。
|
|
7
|
+
*/
|
|
8
|
+
/** 后缀 token → 该后缀声明的上下文窗口。 */
|
|
9
|
+
export const CONTEXT_WINDOW_SUFFIX_TOKENS = {
|
|
10
|
+
'1m': 1_000_000,
|
|
11
|
+
'200k': 200_000,
|
|
12
|
+
};
|
|
13
|
+
const SUFFIX_RE = /\[(1m|200k)\]$/i;
|
|
14
|
+
/** 拆分模型 ID 的基础部分与上下文窗口后缀。 */
|
|
15
|
+
export function parseModelId(modelId) {
|
|
16
|
+
const match = SUFFIX_RE.exec(modelId);
|
|
17
|
+
if (!match)
|
|
18
|
+
return { base: modelId };
|
|
19
|
+
const token = match[1].toLowerCase();
|
|
20
|
+
return {
|
|
21
|
+
base: modelId.slice(0, match.index),
|
|
22
|
+
suffix: `[${token}]`,
|
|
23
|
+
contextWindow: CONTEXT_WINDOW_SUFFIX_TOKENS[token],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** 去掉上下文窗口后缀。仅用于查价/计费/能力表这类按基础 ID 归档的场景。 */
|
|
27
|
+
export function stripContextWindowSuffix(modelId) {
|
|
28
|
+
return modelId.replace(SUFFIX_RE, '');
|
|
29
|
+
}
|
|
30
|
+
/** 该 ID 是否已带网关显式给出的上下文窗口后缀。 */
|
|
31
|
+
export function hasContextWindowSuffix(modelId) {
|
|
32
|
+
return SUFFIX_RE.test(modelId);
|
|
33
|
+
}
|
|
@@ -88,7 +88,7 @@ export function resolveCatalogContext(self, ba) {
|
|
|
88
88
|
: baseagent === 'claude' ? resolveAnthropicConfig(cfg, block.claude)
|
|
89
89
|
: baseagent === 'ecagent' ? resolveEcagentConfig(cfg, block.ecagent) : undefined;
|
|
90
90
|
if (resolved) {
|
|
91
|
-
Object.assign(context, { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey, queryParams: resolved.queryParams,
|
|
91
|
+
Object.assign(context, { baseUrl: resolved.baseUrl, apiKey: resolved.apiKey, authType: 'authType' in resolved ? resolved.authType : undefined, queryParams: resolved.queryParams,
|
|
92
92
|
provider: 'provider' in resolved ? resolved.provider : undefined,
|
|
93
93
|
headers: buildModelRequestHeaders({ baseagent: baseagent, baseUrl: resolved.baseUrl, agentAid: self, configuredHeaders: resolved.headers }) });
|
|
94
94
|
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { getFieldPermission } from '../../config/roles.js';
|
|
2
|
+
import { stripContextWindowSuffix } from './context-window-suffix.js';
|
|
2
3
|
export function isModelAllowedByPatterns(model, allowedModels) {
|
|
3
4
|
if (allowedModels.includes('*'))
|
|
4
5
|
return true;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// 窗口变体是同一模型的不同上下文窗口:白名单写基础 ID(`claude-opus-5`)即同时
|
|
7
|
+
// 授权其窗口变体(`claude-opus-5[1m]`)。反向不成立——白名单显式写了某个变体时,
|
|
8
|
+
// 只授权那一个变体,其它变体和裸 ID 仍被拒绝。
|
|
9
|
+
const candidates = new Set([model, stripContextWindowSuffix(model)]);
|
|
10
|
+
return allowedModels.some(pattern => {
|
|
11
|
+
if (pattern.endsWith('*')) {
|
|
12
|
+
const prefix = pattern.slice(0, -1);
|
|
13
|
+
return [...candidates].some(candidate => candidate.startsWith(prefix));
|
|
14
|
+
}
|
|
15
|
+
return candidates.has(pattern);
|
|
16
|
+
});
|
|
8
17
|
}
|
|
9
18
|
function normalizeBaseagent(baseagent) {
|
|
10
19
|
const normalized = typeof baseagent === 'string' ? baseagent.trim() : '';
|
|
@@ -183,6 +183,10 @@ function substituteManagedTmpDirToken(value) {
|
|
|
183
183
|
}
|
|
184
184
|
return result;
|
|
185
185
|
}
|
|
186
|
+
function containsManagedTmpDirReference(value) {
|
|
187
|
+
return /\$TMPDIR|\$\{TMPDIR\}/.test(value)
|
|
188
|
+
|| /\$env:TMPDIR|\$\{env:TMPDIR\}/i.test(value);
|
|
189
|
+
}
|
|
186
190
|
function restoreManagedTmpDirToken(value, managedTempDir) {
|
|
187
191
|
return value.replaceAll(MANAGED_TMPDIR_TOKEN, managedTempDir ?? '$TMPDIR');
|
|
188
192
|
}
|
|
@@ -362,7 +366,7 @@ function parseFailure(issue, input, offset, tokenIndex, dialect) {
|
|
|
362
366
|
inputLength: input.length,
|
|
363
367
|
};
|
|
364
368
|
}
|
|
365
|
-
function parseLiteralPowerShellCommand(command) {
|
|
369
|
+
function parseLiteralPowerShellCommand(command, options = {}) {
|
|
366
370
|
const input = trimShellSpacing(command);
|
|
367
371
|
if (!input)
|
|
368
372
|
return parseFailure('empty-command', input, 0, 0, 'powershell');
|
|
@@ -372,6 +376,9 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
372
376
|
let token = '';
|
|
373
377
|
let tokenStarted = false;
|
|
374
378
|
let quote = null;
|
|
379
|
+
// Environment variable names are case-insensitive on Windows. Require a
|
|
380
|
+
// complete variable name, including PowerShell's Unicode/name characters.
|
|
381
|
+
const managedTmpDirReference = new RegExp('^\\$(?:env:TMPDIR(?![\\p{L}\\p{Nd}_?:])|\\{env:TMPDIR\\})', process.platform === 'win32' ? 'iu' : 'u');
|
|
375
382
|
const finishToken = () => {
|
|
376
383
|
if (!tokenStarted)
|
|
377
384
|
return;
|
|
@@ -396,6 +403,23 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
396
403
|
}
|
|
397
404
|
continue;
|
|
398
405
|
}
|
|
406
|
+
if (char === '$' && isShellParameterExpansionStart(input[index + 1])) {
|
|
407
|
+
const reference = managedTmpDirReference.exec(input.slice(index))?.[0];
|
|
408
|
+
const next = reference ? input[index + reference.length] : undefined;
|
|
409
|
+
if (!reference) {
|
|
410
|
+
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
411
|
+
}
|
|
412
|
+
if (!options.allowManagedTmpDir || !options.managedTempDir
|
|
413
|
+
|| (next !== undefined && !/[\\/\s"']/u.test(next))) {
|
|
414
|
+
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
415
|
+
}
|
|
416
|
+
// Append the registered value directly: spaces remain in this argument,
|
|
417
|
+
// and characters in the directory are never interpreted as shell syntax.
|
|
418
|
+
token += options.managedTempDir;
|
|
419
|
+
tokenStarted = true;
|
|
420
|
+
index += reference.length - 1;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
399
423
|
if (quote === 'double') {
|
|
400
424
|
if (char === '"') {
|
|
401
425
|
if (!isLiteralArgBoundary(input[index + 1]))
|
|
@@ -412,9 +436,6 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
412
436
|
token += next;
|
|
413
437
|
continue;
|
|
414
438
|
}
|
|
415
|
-
if (char === '$' && isShellParameterExpansionStart(input[index + 1])) {
|
|
416
|
-
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
417
|
-
}
|
|
418
439
|
token += char;
|
|
419
440
|
continue;
|
|
420
441
|
}
|
|
@@ -729,7 +750,7 @@ function doubleQuotedIssueAt(input, index) {
|
|
|
729
750
|
*/
|
|
730
751
|
export function parseLiteralShellCommand(command, options = {}) {
|
|
731
752
|
if (options.dialect === 'powershell') {
|
|
732
|
-
return parseLiteralPowerShellCommand(command);
|
|
753
|
+
return parseLiteralPowerShellCommand(command, options);
|
|
733
754
|
}
|
|
734
755
|
if (options.dialect === 'cmd') {
|
|
735
756
|
return parseLiteralCmdCommand(command, options);
|
|
@@ -1416,16 +1437,16 @@ export function parseCodexExecToolCommand(input) {
|
|
|
1416
1437
|
}
|
|
1417
1438
|
return decoded;
|
|
1418
1439
|
}
|
|
1419
|
-
function
|
|
1440
|
+
function parseProactiveToolCommand(toolName, input) {
|
|
1420
1441
|
const options = {
|
|
1421
1442
|
verifyShellExecutable: process.platform === 'win32',
|
|
1422
1443
|
};
|
|
1423
1444
|
if (toolName === 'Bash' || toolName === 'Shell')
|
|
1424
|
-
return
|
|
1445
|
+
return parseCodexToolCommand(input, options);
|
|
1425
1446
|
if (toolName !== 'exec')
|
|
1426
1447
|
return null;
|
|
1427
1448
|
const command = parseCodexExecToolCommand(input);
|
|
1428
|
-
return command ?
|
|
1449
|
+
return command ? parseCodexToolCommand({ command }, options) : null;
|
|
1429
1450
|
}
|
|
1430
1451
|
function scanBoundedOutputOperators(command) {
|
|
1431
1452
|
const operators = [];
|
|
@@ -1711,13 +1732,13 @@ function findSingleFileOption(argv, startIndex) {
|
|
|
1711
1732
|
const PROACTIVE_SEND_VALUE_FLAGS = new Set([
|
|
1712
1733
|
'--format', '--app', '--file', '--text-from-file', '--link', '--payload',
|
|
1713
1734
|
'--title', '--description', '--text', '--transcript', '--as',
|
|
1714
|
-
'--content-type', '--mention', '--aun-path', '--thread', '--return',
|
|
1735
|
+
'--content-type', '--mention', '--aun-path', '--thread', '--return', '--handoff-context',
|
|
1715
1736
|
]);
|
|
1716
1737
|
const PROACTIVE_SEND_BOOLEAN_FLAGS = new Set([
|
|
1717
1738
|
'--encrypt', '--no-encrypt', '--mention-all',
|
|
1718
1739
|
]);
|
|
1719
1740
|
const SEND_CONTENT_VALUE_FLAGS = new Set([
|
|
1720
|
-
'--payload', '--title', '--description', '--text', '--transcript',
|
|
1741
|
+
'--payload', '--title', '--description', '--text', '--transcript', '--handoff-context',
|
|
1721
1742
|
]);
|
|
1722
1743
|
const TRIGGER_CONTENT_VALUE_FLAGS = new Set(['--prompt']);
|
|
1723
1744
|
/**
|
|
@@ -1728,7 +1749,7 @@ const TRIGGER_CONTENT_VALUE_FLAGS = new Set(['--prompt']);
|
|
|
1728
1749
|
*/
|
|
1729
1750
|
export function containsLiteralManagedTmpDirOutsideSendContent(argv) {
|
|
1730
1751
|
const literalIndexes = argv
|
|
1731
|
-
.map((value, index) => value
|
|
1752
|
+
.map((value, index) => containsManagedTmpDirReference(value) ? index : -1)
|
|
1732
1753
|
.filter(index => index >= 0);
|
|
1733
1754
|
if (literalIndexes.length === 0)
|
|
1734
1755
|
return false;
|
|
@@ -1903,7 +1924,8 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
1903
1924
|
// is represented as `exec` and is handled only for one literal inner call.
|
|
1904
1925
|
if (!selfAid)
|
|
1905
1926
|
return false;
|
|
1906
|
-
const
|
|
1927
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
1928
|
+
const argv = result?.ok ? result.argv : null;
|
|
1907
1929
|
const parsed = argv ? parseEvolcoreSendArgv(argv) : null;
|
|
1908
1930
|
if (!parsed || parsed.action !== 'send')
|
|
1909
1931
|
return false;
|
|
@@ -1914,3 +1936,20 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
1914
1936
|
&& !!argv
|
|
1915
1937
|
&& hasExecutableProactiveSendPayload(argv, expectedScope);
|
|
1916
1938
|
}
|
|
1939
|
+
/** Diagnostic only: this never broadens the exact-send authorization check. */
|
|
1940
|
+
export function describeProactiveSendCommandFailure(toolName, input) {
|
|
1941
|
+
if (!['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
1942
|
+
return undefined;
|
|
1943
|
+
const commandText = [input.command, input.input, ...(Array.isArray(input.commandArgv) ? input.commandArgv : [])]
|
|
1944
|
+
.filter((value) => typeof value === 'string').join(' ');
|
|
1945
|
+
if (!/\bec(?:\.cmd|\.exe)?\s+(?:group|msg)\s+send\b/i.test(commandText))
|
|
1946
|
+
return undefined;
|
|
1947
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
1948
|
+
if (!result)
|
|
1949
|
+
return '当前汇报命令未通过规范解析:只支持单次、字面量的发送调用,请将发送作为独立工具调用。';
|
|
1950
|
+
if (!result.ok) {
|
|
1951
|
+
return `当前汇报命令未通过规范解析(${result.issue},${result.dialect},位置 ${result.offset});`
|
|
1952
|
+
+ '请检查引号和转义,并将发送作为一条独立命令。';
|
|
1953
|
+
}
|
|
1954
|
+
return '当前汇报命令不符合当前会话的发送要求;请核对发送者、私聊或群目标,以及正文和参数。';
|
|
1955
|
+
}
|
|
@@ -5,34 +5,55 @@ import { getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameO
|
|
|
5
5
|
import { resolveRoot } from '../../paths.js';
|
|
6
6
|
let cachedBubblewrapPath;
|
|
7
7
|
let sandboxInitializationCircuit;
|
|
8
|
+
const sessionSandboxInitializationCircuits = new Map();
|
|
8
9
|
let sandboxCircuitNoticeAt = 0;
|
|
9
10
|
export const SANDBOX_INITIALIZATION_FAILED = 'sandbox_initialization_failed';
|
|
10
11
|
export const SANDBOX_INITIALIZATION_COOLDOWN_MS = 5 * 60 * 1000;
|
|
11
12
|
export const SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS = 60 * 1000;
|
|
12
13
|
const SANDBOX_DIAGNOSTIC_TARGET_LIMIT = 256;
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/** Host bootstrap failures open a process circuit; tool-result failures only
|
|
15
|
+
* affect their originating session because tool output is not host attestation. */
|
|
16
|
+
export function recordSandboxInitializationFailure(error, stderr = [], now = Date.now(), sessionId) {
|
|
15
17
|
const detail = createSandboxInitializationError(error, stderr).message.slice(0, 512);
|
|
16
|
-
|
|
18
|
+
getActiveSandboxInitializationFailure(now);
|
|
19
|
+
const previous = sessionId ? sessionSandboxInitializationCircuits.get(sessionId) : sandboxInitializationCircuit;
|
|
20
|
+
const circuit = previous
|
|
17
21
|
? {
|
|
18
|
-
...
|
|
22
|
+
...previous,
|
|
19
23
|
lastObservedAt: now,
|
|
20
|
-
occurrences:
|
|
24
|
+
occurrences: previous.occurrences + 1,
|
|
21
25
|
detail,
|
|
22
26
|
}
|
|
23
27
|
: { openedAt: now, lastObservedAt: now, occurrences: 1, detail };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (sessionId)
|
|
29
|
+
sessionSandboxInitializationCircuits.set(sessionId, circuit);
|
|
30
|
+
else
|
|
31
|
+
sandboxInitializationCircuit = circuit;
|
|
32
|
+
return { ...circuit };
|
|
33
|
+
}
|
|
34
|
+
/** Expire old failures and return the process or requested session circuit. */
|
|
35
|
+
export function getActiveSandboxInitializationFailure(now = Date.now(), cooldownMs = SANDBOX_INITIALIZATION_COOLDOWN_MS, sessionId) {
|
|
36
|
+
for (const [id, circuit] of sessionSandboxInitializationCircuits) {
|
|
37
|
+
if (now - circuit.lastObservedAt >= cooldownMs)
|
|
38
|
+
sessionSandboxInitializationCircuits.delete(id);
|
|
39
|
+
}
|
|
40
|
+
if (sandboxInitializationCircuit && now - sandboxInitializationCircuit.lastObservedAt >= cooldownMs) {
|
|
31
41
|
sandboxInitializationCircuit = undefined;
|
|
32
42
|
sandboxCircuitNoticeAt = 0;
|
|
33
|
-
return undefined;
|
|
34
43
|
}
|
|
35
|
-
|
|
44
|
+
const circuit = sandboxInitializationCircuit ?? (sessionId ? sessionSandboxInitializationCircuits.get(sessionId) : undefined);
|
|
45
|
+
return circuit ? { ...circuit } : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Only recognize the complete native bootstrap diagnostic, never a quotation
|
|
48
|
+
* embedded in ordinary tool output. Tool-result evidence is session-scoped. */
|
|
49
|
+
export function isSandboxInitializationToolResult(content) {
|
|
50
|
+
const text = typeof content === 'string'
|
|
51
|
+
? content
|
|
52
|
+
: Array.isArray(content) && content.every(block => block?.type === 'text' && typeof block.text === 'string')
|
|
53
|
+
? content.map(block => block.text).join('\n')
|
|
54
|
+
: '';
|
|
55
|
+
return /^(?:Exit code [1-9]\d*\r?\n)?apply-seccomp: write \/proc\/self\/(?:uid|gid)_map: (?:Operation not permitted|Permission denied)\s*$/i
|
|
56
|
+
.test(text.trim());
|
|
36
57
|
}
|
|
37
58
|
/** Rate-limit human-readable warnings; structured audit still records every blocked task. */
|
|
38
59
|
export function shouldLogSandboxCircuitNotice(now = Date.now(), intervalMs = SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS) {
|
|
@@ -561,6 +582,7 @@ export function buildBubblewrapCommand(executable, executableArgs, options) {
|
|
|
561
582
|
return { command: bubblewrapPath, args };
|
|
562
583
|
}
|
|
563
584
|
export function _resetSandboxRuntimeCache() {
|
|
585
|
+
sessionSandboxInitializationCircuits.clear();
|
|
564
586
|
cachedBubblewrapPath = undefined;
|
|
565
587
|
sandboxInitializationCircuit = undefined;
|
|
566
588
|
sandboxCircuitNoticeAt = 0;
|
|
@@ -2209,6 +2209,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2209
2209
|
}
|
|
2210
2210
|
const parserOptions = {
|
|
2211
2211
|
verifyShellExecutable: process.platform === 'win32',
|
|
2212
|
+
managedTempDir: context.managedTempDir,
|
|
2212
2213
|
...(context.delegationCarrier
|
|
2213
2214
|
? { expectedDelegationToken: context.delegationCarrier }
|
|
2214
2215
|
: {}),
|
|
@@ -2320,7 +2321,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2320
2321
|
}
|
|
2321
2322
|
if (ecCommand.issue === 'unsafe-expansion') {
|
|
2322
2323
|
const message = shellDialect === 'powershell'
|
|
2323
|
-
? '🔒 EC
|
|
2324
|
+
? '🔒 EC 命令包含无法安全解析的 PowerShell 展开;临时路径仅支持已登记的当前会话 $env:TMPDIR 或 ${env:TMPDIR},也可使用完整路径。纯文字请使用单引号,或用 PowerShell 反引号转义 $ 和正文内的双引号。'
|
|
2324
2325
|
: shellDialect === 'cmd'
|
|
2325
2326
|
? '🔒 EC 命令包含未转义的 cmd.exe 展开;请不要使用 %VAR%、!VAR! 或 ^ 转义。cmd.exe 传给 ec 时必须保持原文字面量'
|
|
2326
2327
|
: '🔒 EC 双引号正文包含未转义的 Shell 展开;请将反引号写成 \\`,将 $()、${}、$变量中的 $ 写成 \\$。Shell 传给 ec 时会还原为原文字面量';
|