@sema-agent/server 7.113.0 → 7.115.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/MIGRATION.md +109 -2
- package/USAGE.md +36 -3
- package/deploy/sema-up/cluster-up.sh +13 -1
- package/deploy/sema-up/kube-up.sh +13 -1
- package/deploy/sema-up/preflight.sh +13 -1
- package/deploy/sema-up/sema-up.sh +13 -1
- package/deploy/sema-up/smoke.sh +13 -1
- package/dist/adoption/wire.d.ts +0 -3
- package/dist/approval-card.d.ts +21 -24
- package/dist/approval-card.js +12 -0
- package/dist/approval.js +5 -2
- package/dist/boot/exit-boundary.d.ts +3 -2
- package/dist/boot/exit-boundary.js +3 -0
- package/dist/boot/memory-boundary.d.ts +2 -1
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/resolve-spec.js +6 -3
- package/dist/boot/shutdown.d.ts +19 -0
- package/dist/boot/shutdown.js +12 -1
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/config-catalog.js +8 -6
- package/dist/config-center/apply-effective.js +11 -11
- package/dist/config-invariants.d.ts +27 -0
- package/dist/config-invariants.js +54 -39
- package/dist/config-provider.d.ts +16 -0
- package/dist/config-provider.js +11 -2
- package/dist/config-types.d.ts +24 -11
- package/dist/config-types.js +0 -1
- package/dist/config.d.ts +41 -34
- package/dist/config.js +78 -81
- package/dist/deployment-governance.d.ts +15 -4
- package/dist/deployment-governance.js +20 -2
- package/dist/env-facts.d.ts +3 -1
- package/dist/env-facts.js +5 -1
- package/dist/execution-lane-caps.d.ts +54 -30
- package/dist/execution-lane-caps.js +48 -20
- package/dist/fleet/fleet-bus.d.ts +1 -22
- package/dist/hooks/hook-runner.d.ts +112 -6
- package/dist/hooks/hook-runner.js +138 -32
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +9 -4
- package/dist/http/approval-sweeps.js +20 -0
- package/dist/http/checkpoint-ctx.d.ts +68 -0
- package/dist/http/checkpoint-ctx.js +36 -0
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/resume-legs.js +9 -0
- package/dist/http/route-ctx.d.ts +7 -0
- package/dist/http/routes/a2a-serve.d.ts +1 -3
- package/dist/http/routes/a2a-serve.js +0 -2
- package/dist/http/routes/admin-memory-optout.d.ts +0 -2
- package/dist/http/routes/admin-memory-optout.js +0 -1
- package/dist/http/routes/capabilities.js +3 -2
- package/dist/http/routes/diagnostics.d.ts +5 -0
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/tasks.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +2 -1
- package/dist/local-data-root.d.ts +1 -1
- package/dist/mcp-content-origin.d.ts +0 -8
- package/dist/model-entry-refusal.d.ts +10 -2
- package/dist/model-entry-refusal.js +19 -3
- package/dist/observability/corrupt-read-seat.d.ts +3 -3
- package/dist/observability/corrupt-read-seat.js +5 -4
- package/dist/observability/fail-open.d.ts +10 -9
- package/dist/observability/fail-open.js +5 -1
- package/dist/permission-rule-list.d.ts +81 -0
- package/dist/permission-rule-list.js +17 -0
- package/dist/plugins/remote-env-host.d.ts +0 -35
- package/dist/plugins/remote-env-host.js +0 -8
- package/dist/plugins/remote-env-local-docker.d.ts +5 -13
- package/dist/plugins/remote-env-local-docker.js +2 -4
- package/dist/run-local.js +4 -1
- package/dist/runtime-governance.d.ts +21 -1
- package/dist/runtime-governance.js +6 -1
- package/dist/security.js +2 -2
- package/dist/session-policy-wire.d.ts +5 -4
- package/dist/store-posture.d.ts +7 -7
- package/dist/store-posture.js +0 -1
- package/dist/task-settings.d.ts +19 -5
- package/dist/task-settings.js +19 -30
- package/dist/tool-approval.d.ts +16 -4
- package/dist/tool-approval.js +2 -1
- package/dist/trace/core-keyset-guard.d.ts +9 -4
- package/dist/trace/project.js +9 -0
- package/package.json +4 -4
- package/scripts/{retired-mode-seed-drain-local.mjs → drain-check-local.mjs} +147 -26
|
@@ -1,16 +1,13 @@
|
|
|
1
|
+
import { type ExecutionLane as DeclarableLane, type ExecutionLaneRow } from "@sema-agent/settings-schema/execution-lanes";
|
|
1
2
|
import type { ServiceConfigFlat } from "./config-types.js";
|
|
2
3
|
/** 一位能力的三态。`single-user-only` = 车道支持,但只在单用户部署(`requirePrincipal !== true`)开 ——
|
|
3
4
|
* 多租户下这一位必须关(host 家族的 cwd/scheduler/backgroundShell/LSP 都是这一档,理由同源:
|
|
4
5
|
* 一个租户的输入不许把 agent 指向 worker 宿主上另一个租户/运维的文件与进程)。 */
|
|
5
6
|
export type LaneCapability = "supported" | "unsupported" | "single-user-only";
|
|
6
|
-
/** `REMOTE_EXEC`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
export type RemoteExecProvider = NonNullable<ServiceConfigFlat["remoteExec"]>["provider"];
|
|
11
|
-
/** 闭集:执行车道全集 = provider 词 + `in-process`(`REMOTE_EXEC` 未设的进程内 stub 形,配置面无 provider
|
|
12
|
-
* 可派生 —— 它是「没有 remoteExec」这个状态的名字)。加词 ⇒ {@link executionLaneCaps} 的穷举 switch 与
|
|
13
|
-
* {@link EXECUTION_LANE_WORDS} 两处都编译红,这正是要的(「加词必须逐位表态」)。 */
|
|
7
|
+
/** `REMOTE_EXEC` 能写下去的车道词全集 = settings-schema 词表正本的 `ExecutionLane`(**派生**,本仓零手抄)。 */
|
|
8
|
+
export type RemoteExecProvider = DeclarableLane;
|
|
9
|
+
/** 闭集:执行车道全集 = 表的可声明车道 + `in-process`(`REMOTE_EXEC` 未设的进程内 stub 形 —— 它是「没有 remoteExec」
|
|
10
|
+
* 这个状态的名字,不在表里)。表加词 ⇒ {@link executionLaneCaps} 的穷举 switch 编译红,这正是要的(「加词必须逐位表态」)。 */
|
|
14
11
|
export type ExecutionLane = "in-process" | RemoteExecProvider;
|
|
15
12
|
/**
|
|
16
13
|
* per-lane 能力位。每一位的注里点名**今天真正做判决的那处代码**——对账测试按这些坐标跑真函数,
|
|
@@ -88,12 +85,13 @@ export interface ExecutionLaneCaps {
|
|
|
88
85
|
* · `single-user-only` = 所有任务落在**同一个共享目标**上(worker 宿主:host;一台常驻 SSH 对端:ssh;一台 ADB 设备:adb)
|
|
89
86
|
* ⇒ 只许单用户部署用。
|
|
90
87
|
* 本位**没有** `unsupported` 这一档(那会读成「谁都不许用」,不是车道事实)—— 型上收窄掉,每条车道必须在两档里表态。
|
|
91
|
-
* 🔴
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
88
|
+
* 🔴 **7.114.0 起本位的值不在本仓写**:七条可声明车道读 settings-schema 词表正本的表行(`executionLaneRow(lane).tenantSeparation`;
|
|
89
|
+
* 中心写闸的 `requirePrincipal ∧ single-user-only` 交叉判与本仓 `multi-tenant-shared-lane` 拒启读的是同一个值,不会分叉);
|
|
90
|
+
* 只有 `in-process` 一格是本仓自己的事实(它不在表里)。
|
|
91
|
+
* 唯一消费点是 `config-invariants.ts` 的 `multi-tenant-shared-lane`(经 {@link laneCapabilityHolds} 落成布尔)。本位值与本仓**运行期事实**
|
|
92
|
+
* 不矛盾的对账 = `test/execution-lanes-schema-conformance.test.ts`(隔离位 / 共享文件平面 ⇒ 租户档的蕴含 + 唯一一条「不隔离却分租户」
|
|
93
|
+
* 的车道 = device 点名);ssh / adb 「两租户同一目标」的行为预言机 = `test/multi-tenant-shared-lane.test.ts`
|
|
94
|
+
* (两个租户的任务铸出的 env 报同一个 `workspaceHandle().sandboxId`)。
|
|
97
95
|
*/
|
|
98
96
|
readonly tenantSeparation: Exclude<LaneCapability, "unsupported">;
|
|
99
97
|
}
|
|
@@ -117,23 +115,49 @@ export interface ExecutionLaneCaps {
|
|
|
117
115
|
* 恰是诚实形(§4.3.4)。
|
|
118
116
|
*/
|
|
119
117
|
export declare function executionLaneCaps(lane: ExecutionLane): ExecutionLaneCaps;
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
/**
|
|
119
|
+
* 车道普查全集 = 本仓内部状态名 `in-process` + 词表正本的可声明车道(次序 = 正本声明序;对账测试按它逐 lane 跑真判别式)。
|
|
120
|
+
* 由正本派生、不手维护(手维护的「全表」已被代码反证漏过行)。
|
|
121
|
+
*/
|
|
123
122
|
export declare const ALL_EXECUTION_LANES: readonly ExecutionLane[];
|
|
123
|
+
/** 一个开放字符串是不是闭集里的词(不做前缀 / 大小写宽容)。 */
|
|
124
|
+
export declare function isExecutionLane(word: string): word is ExecutionLane;
|
|
124
125
|
/**
|
|
125
|
-
* `REMOTE_EXEC` **真能写下去的**那几个词
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* `enumValues: ["e2b","k8s",…]` 字面量(字段静态型是 `readonly string[]`,与 {@link RemoteExecProvider}
|
|
130
|
-
* 零类型联系)。病形与 S-382 逮到的 `WEB_SEARCH_PROVIDER` 逐字同族:给
|
|
131
|
-
* `ServiceConfigFlat["remoteExec"]` 加一条 provider 臂(config-types.ts 里写着的下一条就是
|
|
132
|
-
* `remote-docker`),本文件的穷举 switch 与词表**会**编译红,而目录行不会 —— 于是运维在
|
|
133
|
-
* `GET /v1/config/catalog` 上读到的可写词集静默少一个,新车道成了「配了但目录说不认识」。
|
|
134
|
-
* 收成一只派生数组之后,目录行**就用它本人**(身份相等有钉),全仓只剩这一张表。
|
|
126
|
+
* `REMOTE_EXEC` **真能写下去的**那几个词 = settings-schema 词表正本的 `EXECUTION_LANE_WORDS`(冻结副本,次序逐字同)。
|
|
127
|
+
* 读者:配置目录 `REMOTE_EXEC` 行的 `enumValues`(**就用它本人**,身份相等有钉 —— S-426 修过「目录手抄一份词表」的病)、
|
|
128
|
+
* [ref] 拒启句的 `valid:` 列表、`REQUIRE_EXECUTION_LANE` 拒启句的出路、`multi-tenant-shared-lane` 拒启句的出路车道。
|
|
129
|
+
* 7.114.0 起全仓唯一的车道词来源是正本;本常量只是它在本仓的那一个读口。
|
|
135
130
|
*/
|
|
136
131
|
export declare const REMOTE_EXEC_PROVIDER_WORDS: readonly RemoteExecProvider[];
|
|
132
|
+
/** 一条可声明车道在词表正本里的那一行(必需 / 可选 env 四栏 + 租户档)。加载期自检过完备性,所以对任何 {@link RemoteExecProvider} 都有行。 */
|
|
133
|
+
export declare function executionLaneRow(lane: RemoteExecProvider): ExecutionLaneRow;
|
|
134
|
+
/**
|
|
135
|
+
* 一条车道**必需**的 env 键名 = 正本表行的 `requiredEnv ∪ requiredSecretEnv`(次序:非密坐标在前、密钥在后 —— 与 7.113.0 本仓手写表逐字同)。
|
|
136
|
+
* 读者:`config.ts` 的 [ref] 拒启门(点了名却缺件 ⇒ 点名缺哪几键)、部署面门(`test/deploy-lanes-contract.test.ts`:`lanes.json` 每行登记
|
|
137
|
+
* 本车道必需键、根 / review compose 透传全部车道必需键之并)、`scripts/gen-lanes-json.mjs`(从同一张正本生成 `lanes.json` 的必需键列)。
|
|
138
|
+
* device 没有必需 env(设备是运行期连进来的;它的 boot 前提是跨键的,在跨域不变量区拒启)。
|
|
139
|
+
*/
|
|
140
|
+
export declare function laneRequiredEnv(lane: RemoteExecProvider): readonly string[];
|
|
141
|
+
/**
|
|
142
|
+
* S-598 件③ / S-1008 —— **「`REQUIRE_EXECUTION_LANE=true` ∧ 没有任何车道被点名 ⇒ 拒启」的判据与拒因句的唯一属主**(`modelProviderRefusal` /
|
|
143
|
+
* `modelRouteEndpointRefusal` 同形:属主在叶模块,执法点各自调它,登记在 `config-invariants.ts`)。
|
|
144
|
+
*
|
|
145
|
+
* 🔴 病(S-1008,test d327-g6-engine-7112 OBS-4 / OBS-5;2026-10-03 亲核):修前**两份句子**。运行期真拒启的是 `config.ts` 手写 throw
|
|
146
|
+
* (点名 `REQUIRE_EXECUTION_LANE` / `REMOTE_EXEC` + 修法,用户在 stderr 读到的就是它);注册表条目另写了一句短的
|
|
147
|
+
* 「the deployment asked to refuse this state」,而它只在 `config-center/stage-limits.ts` 的全表校验里被调 —— 候选视图只装
|
|
148
|
+
* `BATCH1_LIMIT_KEYS` 的限额键,带不上这两键 ⇒ 对本条恒 `next === live`,**那一句没有任何读者**(test 六个拒启格 stderr 里零命中)。
|
|
149
|
+
* 改一份另一份不跟。现形:boot 的 throw 与注册表条目的 `check` **都**调本函数 —— 一只判据、一句话,没有第二份文本可漂。
|
|
150
|
+
*
|
|
151
|
+
* 判据读**装配结果**(`executionLaneOf` = 全仓唯一的缺席归一读口,缺席 ⇒ `in-process`),不重判 env:`remoteExec` 缺席**就是**
|
|
152
|
+
* 「`REMOTE_EXEC` 未设/空串 ∧ `CONFIG_PROVIDER ≠ local`」这条三元链尾臂的落点;点了名却配不齐 / 认不出归 [ref] 那条既有拒启(两句话各说各的问题)。
|
|
153
|
+
*
|
|
154
|
+
* 句子的量词纪律(S-1008 ②):stub 车道上**手带族**(`wiring_manifest.tools.entries[].mountedBy === "hands"`)整族不挂,名册里其余工具
|
|
155
|
+
* 照常执行(test R1n:`Now` 真执行成功)—— 不写「零工具」;名册**总条数**随 core 版本走,不写进句子(S-1008 ③)。
|
|
156
|
+
*/
|
|
157
|
+
export declare function executionLaneRequiredRefusal(deployment: {
|
|
158
|
+
readonly requireExecutionLane: boolean;
|
|
159
|
+
readonly remoteExec?: ServiceConfigFlat["remoteExec"];
|
|
160
|
+
}): string | undefined;
|
|
137
161
|
/**
|
|
138
162
|
* `config.remoteExec?.provider` → 车道词。**全仓唯一**的缺席归一读口(S-602 门 D:`?? "in-process"` 只许住在这里,
|
|
139
163
|
* `?? "host"` 与 `lane === undefined || lane === "host"` 同义折零容忍)。
|
|
@@ -144,9 +168,9 @@ export declare const REMOTE_EXEC_PROVIDER_WORDS: readonly RemoteExecProvider[];
|
|
|
144
168
|
*
|
|
145
169
|
* 两个重载 = 类型收窄([ref] §2.2 A):喂**已装配的** config 形(`RemoteExecProvider | undefined`)⇒
|
|
146
170
|
* 返回型里没有 `undefined`,调用点不必为一条不可达的臂写兜底(兜底写多了就是下一处折词);喂开放字符串
|
|
147
|
-
* (测试、外部输入)⇒ 返回含 `undefined
|
|
148
|
-
* `RemoteExecProvider
|
|
149
|
-
* `
|
|
171
|
+
* (测试、外部输入)⇒ 返回含 `undefined`,逼调用点表态。第一个重载的成立依据是**构造**的:
|
|
172
|
+
* `RemoteExecProvider` 是正本元组 `EXECUTION_LANE_WORDS` 的元素型,而 {@link isExecutionLane} 的集合正是由那只元组
|
|
173
|
+
* (加 `in-process`)建成 ⇒ 对 `RemoteExecProvider` 的每一个值 `isExecutionLane` 恒真(对账格逐词跑过)。
|
|
150
174
|
*/
|
|
151
175
|
export declare function executionLaneOf(provider: RemoteExecProvider | undefined): ExecutionLane;
|
|
152
176
|
export declare function executionLaneOf(provider: string | undefined): ExecutionLane | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
|
+
import { EXECUTION_LANE_WORDS as DECLARABLE_LANE_WORDS, EXECUTION_LANES } from "@sema-agent/settings-schema/execution-lanes";
|
|
2
3
|
const NO = "unsupported";
|
|
3
4
|
const YES = "supported";
|
|
4
5
|
const SU = "single-user-only";
|
|
@@ -38,7 +39,7 @@ export function executionLaneCaps(lane) {
|
|
|
38
39
|
hostFilePlane: YES,
|
|
39
40
|
coreRemoteExecutionEnv: YES,
|
|
40
41
|
canonicalPathAuthority: YES,
|
|
41
|
-
tenantSeparation:
|
|
42
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
42
43
|
};
|
|
43
44
|
case "e2b":
|
|
44
45
|
return {
|
|
@@ -56,7 +57,7 @@ export function executionLaneCaps(lane) {
|
|
|
56
57
|
hostFilePlane: NO,
|
|
57
58
|
coreRemoteExecutionEnv: YES,
|
|
58
59
|
canonicalPathAuthority: NO,
|
|
59
|
-
tenantSeparation:
|
|
60
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
60
61
|
};
|
|
61
62
|
case "k8s":
|
|
62
63
|
return {
|
|
@@ -74,7 +75,7 @@ export function executionLaneCaps(lane) {
|
|
|
74
75
|
hostFilePlane: NO,
|
|
75
76
|
coreRemoteExecutionEnv: YES,
|
|
76
77
|
canonicalPathAuthority: YES,
|
|
77
|
-
tenantSeparation:
|
|
78
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
78
79
|
};
|
|
79
80
|
case "ssh":
|
|
80
81
|
return {
|
|
@@ -92,7 +93,7 @@ export function executionLaneCaps(lane) {
|
|
|
92
93
|
hostFilePlane: NO,
|
|
93
94
|
coreRemoteExecutionEnv: YES,
|
|
94
95
|
canonicalPathAuthority: YES,
|
|
95
|
-
tenantSeparation:
|
|
96
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
96
97
|
};
|
|
97
98
|
case "adb":
|
|
98
99
|
return {
|
|
@@ -110,7 +111,7 @@ export function executionLaneCaps(lane) {
|
|
|
110
111
|
hostFilePlane: NO,
|
|
111
112
|
coreRemoteExecutionEnv: YES,
|
|
112
113
|
canonicalPathAuthority: YES,
|
|
113
|
-
tenantSeparation:
|
|
114
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
114
115
|
};
|
|
115
116
|
case "local-docker":
|
|
116
117
|
return {
|
|
@@ -128,7 +129,7 @@ export function executionLaneCaps(lane) {
|
|
|
128
129
|
hostFilePlane: NO,
|
|
129
130
|
coreRemoteExecutionEnv: YES,
|
|
130
131
|
canonicalPathAuthority: NO,
|
|
131
|
-
tenantSeparation:
|
|
132
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
132
133
|
};
|
|
133
134
|
case "device":
|
|
134
135
|
return {
|
|
@@ -146,25 +147,52 @@ export function executionLaneCaps(lane) {
|
|
|
146
147
|
hostFilePlane: NO,
|
|
147
148
|
coreRemoteExecutionEnv: YES,
|
|
148
149
|
canonicalPathAuthority: YES,
|
|
149
|
-
tenantSeparation:
|
|
150
|
+
tenantSeparation: executionLaneRow(lane).tenantSeparation,
|
|
150
151
|
};
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
host: true,
|
|
156
|
-
e2b: true,
|
|
157
|
-
k8s: true,
|
|
158
|
-
ssh: true,
|
|
159
|
-
adb: true,
|
|
160
|
-
"local-docker": true,
|
|
161
|
-
device: true,
|
|
162
|
-
};
|
|
154
|
+
export const ALL_EXECUTION_LANES = Object.freeze(["in-process", ...DECLARABLE_LANE_WORDS]);
|
|
155
|
+
const EXECUTION_LANE_SET = new Set(ALL_EXECUTION_LANES);
|
|
163
156
|
export function isExecutionLane(word) {
|
|
164
|
-
return
|
|
157
|
+
return EXECUTION_LANE_SET.has(word);
|
|
158
|
+
}
|
|
159
|
+
export const REMOTE_EXEC_PROVIDER_WORDS = Object.freeze([...DECLARABLE_LANE_WORDS]);
|
|
160
|
+
const LANE_ROWS = (() => {
|
|
161
|
+
const rows = new Map();
|
|
162
|
+
for (const row of EXECUTION_LANES) {
|
|
163
|
+
if (rows.has(row.lane))
|
|
164
|
+
throw new Error(`@sema-agent/settings-schema EXECUTION_LANES lists lane "${row.lane}" twice — the lane table is the single source; refusing to guess which row is meant`);
|
|
165
|
+
rows.set(row.lane, row);
|
|
166
|
+
}
|
|
167
|
+
const missing = DECLARABLE_LANE_WORDS.filter((lane) => !rows.has(lane));
|
|
168
|
+
if (missing.length > 0)
|
|
169
|
+
throw new Error(`@sema-agent/settings-schema EXECUTION_LANES has no row for lane(s) ${missing.join(", ")} — the lane table is the single source; refusing to start with a lane whose required env / tenant separation is unknown`);
|
|
170
|
+
return rows;
|
|
171
|
+
})();
|
|
172
|
+
export function executionLaneRow(lane) {
|
|
173
|
+
const row = LANE_ROWS.get(lane);
|
|
174
|
+
if (row === undefined)
|
|
175
|
+
throw new Error(`execution lane "${lane}" has no row in the settings-schema lane table (load-time completeness check was bypassed)`);
|
|
176
|
+
return row;
|
|
177
|
+
}
|
|
178
|
+
export function laneRequiredEnv(lane) {
|
|
179
|
+
const row = executionLaneRow(lane);
|
|
180
|
+
return [...row.requiredEnv, ...row.requiredSecretEnv];
|
|
181
|
+
}
|
|
182
|
+
export function executionLaneRequiredRefusal(deployment) {
|
|
183
|
+
if (!deployment.requireExecutionLane || executionLaneOf(deployment.remoteExec?.provider) !== "in-process")
|
|
184
|
+
return undefined;
|
|
185
|
+
return (`REQUIRE_EXECUTION_LANE=true but NO execution lane is named on this deployment — refusing to start. ` +
|
|
186
|
+
`With no lane the engine falls back to its in-process stub environment: the hands band (the roster entries whose ` +
|
|
187
|
+
`wiring_manifest mountedBy is "hands", Bash among them) is not mounted, while the rest of the roster still executes, ` +
|
|
188
|
+
`and every task still returns status=completed — for any request that needs a hand, indistinguishable from "the model ` +
|
|
189
|
+
`chose not to use its tools". (How much of the roster is mounted is a per-run fact: read the wiring_manifest event and ` +
|
|
190
|
+
`count entries whose mountedBy is "hands" — never judge by tool names.) ` +
|
|
191
|
+
`Cause: REMOTE_EXEC is unset/empty AND CONFIG_PROVIDER is not "local". ` +
|
|
192
|
+
`Fix: set REMOTE_EXEC to one of ${REMOTE_EXEC_PROVIDER_WORDS.join(" | ")} (and that lane's required env), ` +
|
|
193
|
+
`or set CONFIG_PROVIDER=local to take the single-machine \`host\` default, ` +
|
|
194
|
+
`or unset REQUIRE_EXECUTION_LANE to keep running without hands on purpose.`);
|
|
165
195
|
}
|
|
166
|
-
export const ALL_EXECUTION_LANES = Object.freeze(Object.keys(EXECUTION_LANE_WORDS).filter(isExecutionLane));
|
|
167
|
-
export const REMOTE_EXEC_PROVIDER_WORDS = Object.freeze(ALL_EXECUTION_LANES.filter((lane) => lane !== "in-process"));
|
|
168
196
|
export function executionLaneOf(provider) {
|
|
169
197
|
if (provider === undefined)
|
|
170
198
|
return "in-process";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { TaskNotificationPayload, TerminalCause, WorkflowRun } from "@sema-agent/core";
|
|
2
|
-
import type { RunRecord
|
|
3
|
-
import type { AssertAllKeysHandled } from "../trace/core-keyset-guard.js";
|
|
2
|
+
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
4
3
|
/** [ref] 幽灵行案的单源判别:一条 `task_notification` 只有在 **agent 族 × 终态** 时才允许打
|
|
5
4
|
* `onChildTerminal`(fleet「subagent 树」只渲 agent 子代)。`background_bash`/`monitor`/`external`
|
|
6
5
|
* 不属 agent fleet 树——它们此前每条都打,fleet-bus 的「无 tick 无 claim」臂给 b\* 与 m\* handle 合成
|
|
@@ -674,24 +673,4 @@ export declare function isTerminalFleetStatus(status: FleetTaskStatus): boolean;
|
|
|
674
673
|
export declare function runStatusToFleet(status: RunStatusForFleet, opts?: {
|
|
675
674
|
awaitingPlanApproval?: boolean;
|
|
676
675
|
}): FleetTaskStatus;
|
|
677
|
-
/**
|
|
678
|
-
* S-213③ —— **编译期同源钉**:fleet 的 `"failed"` 桶必须恰好收下 `RUN_STATUS_CLASS` 判为「非成功终局」
|
|
679
|
-
* 的每一个 core 词。
|
|
680
|
-
*
|
|
681
|
-
* 这个词的病史:`blocked` 曾在本仓有**两个独立写者**且答案互斥 —— 本文件读它为「非成功终局(agent
|
|
682
|
-
* 放弃)⇒ 行离场」,而 `observability/run-terminal.ts` 读它为「治理判决,不是失败 ⇒ 不打行」。两处
|
|
683
|
-
* 各自穷举、各自正确地编译,结果是一条 `report_blocked` 的 run 在面板上离场、在引擎日志里一个字都没有。
|
|
684
|
-
* 语义收归 `plugins/store-contracts.ts` 的那张表之后,本行不再自持判断:表把某个词挪出/挪进
|
|
685
|
-
* `unsuccessful`(或 core 新增一个非成功终态)而上面的 switch 没跟上 ⇒ 下面这条收不敛到 `never` ⇒ tsc 红。
|
|
686
|
-
* (`"error"` 是 server 自有别名、不在那张表的辖域内,故不在本钉的两边出现。)
|
|
687
|
-
*/
|
|
688
|
-
type FleetUnsuccessfulBucket = "failed" | "blocked";
|
|
689
|
-
type _GuardFleetUnsuccessfulBucket = AssertAllKeysHandled<Exclude<UnsuccessfulRunStatus, FleetUnsuccessfulBucket>>;
|
|
690
|
-
/** 反向门(与 `core-keyset-guard.ts` 的 `_Guard*Reverse` 同形):桶里留了一个表**不再**判为非成功终局的
|
|
691
|
-
* 词同样红 —— 只有正向门时,把 `blocked` 挪回 `succeeded` 而 switch 照旧塞进 failed 桶,并集覆盖、
|
|
692
|
-
* 差集门一声不吭(本批实测:只加正向门时该变异 tsc rc=0)。两道钉一起加。 */
|
|
693
|
-
type _GuardFleetUnsuccessfulBucketReverse = AssertAllKeysHandled<Exclude<FleetUnsuccessfulBucket, UnsuccessfulRunStatus>>;
|
|
694
|
-
/** 占位使用(防 noUnusedLocals 形 lint;类型别名本身零运行时)。 */
|
|
695
|
-
export type FleetVocabGuards = [_GuardFleetUnsuccessfulBucket, _GuardFleetUnsuccessfulBucketReverse];
|
|
696
|
-
export {};
|
|
697
676
|
//# sourceMappingURL=fleet-bus.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HooksConfig } from "@sema-agent/settings-schema/hooks";
|
|
1
|
+
import { HooksConfig, type HookEventName } from "@sema-agent/settings-schema/hooks";
|
|
2
2
|
import type { Hooks } from "@sema-agent/core";
|
|
3
3
|
import type { Logger } from "../observability/logger.js";
|
|
4
4
|
import type { HookNoticeObservation } from "../fleet/fleet-bus.js";
|
|
@@ -36,17 +36,123 @@ export interface ParseHooksConfigResult {
|
|
|
36
36
|
error?: string;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* 🔴 **S-924 → S-995(clay [ref] ③;八病审计 SG H6-d;test 7.112.0 验收 OBS ①)—— 工具事件的 matcher 选中了工具**别名**、却选不中它的正名 ⇒ fresh 提交面响亮拒。**
|
|
40
|
+
*
|
|
41
|
+
* 病:三个工具事件(PreToolUse / PostToolUse / PostToolUseFailure)的 matcher 都按调用的 **wire 名**匹配(本文件顶注
|
|
42
|
+
* 「tool_name 是哪一个名」;core `NAME_READINGS` 三行均为 `wire`)。引擎自带工具 `ListAgents` 的别名 `ListPeers` 写进 matcher
|
|
43
|
+
* ⇒ **永不命中、一句话都没有**(post 两事件自 7.107.0 / core 7.36.0 起如此,PreToolUse 一直如此)。静默不命中 = 写的人以为在生效的 hook
|
|
44
|
+
* 其实没跑,硬破坏三句要求的「响亮」没有着落。7.112.0(S-924)只拒**字面**别名;同一个错误写成 `^ListPeers$` / `ListPeers.*` / `(ListPeers)` 照样 200(S-995)。
|
|
45
|
+
*
|
|
46
|
+
* 规则(一条;字面形是它的特例):{@link deadToolMatcherIssue} 对 core 静态目录里**每一条退役别名**问「这只 matcher 选不选它 / 选不选它的正名」:
|
|
47
|
+
* matcher **命中别名 ∧ 不命中它的正名** ⇒ `400 request.field_invalid`(`admission.ts` fresh 门)。「选不选」与运行期 {@link hookMatcherMatches} 同一套读法,按**一条顺序**判(两档共用,7.114.0 修复轮):
|
|
48
|
+
* ① **长度上限**:超 {@link MAX_MATCHER_EVAL_CHARS} 的 matcher 两档都不读 ⇒ 未判(reason = oversize);
|
|
49
|
+
* ② 空 / `*` 命中一切 ⇒ 永不「死」;
|
|
50
|
+
* ③ **字面 / 名字表读法**({@link matcherLiteralNames},与运行期共用:`|` 与 `,` 分隔、每段 trim〔容忍两端与分隔符两侧空白〕、不折大小写、不剥词内空白)
|
|
51
|
+
* 两档都先走、每只 matcher 读一次、**不占求值预算**,按名字表成员答 —— 所以两档在字面子集上答案逐位相同是构造出来的(R5f 语料再钉);
|
|
52
|
+
* ④ 只剩**正则形**:**`"regex"` 档**(hooks 会被采纳的车道,`taskHooksHonored`)问运行期正则臂的那一只函数 {@link matcherPatternMatches}(同一个函数对象,锚定正则),
|
|
53
|
+
* 经 `node:vm` 的 `timeout` 带一次受理共享的预算调用({@link matcherPatternMatchesWithin});**`"literal"` 档**(多租户车道:hooks 整只被 resolveSpec 忽略)
|
|
54
|
+
* = **不适用**:该车道没有被守的对象(hooks 不装配),不判、不算未判、不登记 —— **零正则求值、零调用方正则执行**(7.112.0 的字面判保留:裸 `ListPeers` / `Read|ListPeers` 照旧 400)。
|
|
55
|
+
* · **为什么两档**(codex r1 [high] + 主会话裁):受理面是**所有**车道,而运行期只有单用户车道跑 hooks。在受理线程上跑调用方的正则 = 给多租户加一个事件循环 DoS 面
|
|
56
|
+
* (实测:`(.*)*` ×10 + `X` 对 9 字符名字首次执行 1.7 s,18 字符嵌套形 15 s,24 字符 157 s;8 MiB 交替串编译 + 求值 9 s)—— 预算只界住一次请求、界不住「每个请求再拿一份」。
|
|
57
|
+
* 但也不能因此在多租户车道「不判决」(那会放宽已发行为:裸 `ListPeers` 的 400)。所以两车道同一条规则、两档深度。谓词 `taskHooksHonored` 是唯一属主(与 resolveSpec / capabilities 同调)。
|
|
58
|
+
* · **为什么不进 {@link parseHooksConfig}**(codex r1 [high],S-924):`parseHooksConfig` 同时是 resume 重放(`parseTaskSettings` 宽容层)的校验器,
|
|
59
|
+
* 它判不过 ⇒ **整只 hooks 不采纳**。存量 body 里同一份 hooks 常常还有会真拦的 PreToolUse 钩子 —— 一个永不命中的别名 matcher 把它们一并带走,
|
|
60
|
+
* 升级前被拦的 Bash 升级后放行(安全轴上的回退)。所以这条规则住在 fresh 专用的判据里,与 `taskSettingsKeyIssue` 同姿势
|
|
61
|
+
* (「fresh 400 / resume 宽容」双层:会砖存量的规则只咬 fresh)。resume 照旧整只采纳,别名那一组继续永不命中(与 7.111.0 逐字同),其余照跑。
|
|
62
|
+
* · **哪些事件** = 契约 `HOOK_EVENT_MATCHER_FIELD[event] === "tool_name"` ∧ 引擎属主 `HOOK_EVENT_OWNER[event] === "engine"`
|
|
63
|
+
* (今天恰是上面三个;`PermissionRequest` / `PermissionDenied` 也匹配 `tool_name` 但属壳,壳自己跑,不是本 runner 的读法)。**不手抄事件表**([ref])。
|
|
64
|
+
* · **哪些名字算别名** = core 静态目录(`describeToolCatalog()`)里某条 `face.aliases` 的成员,且它自己不是另一条目的 wire 名({@link engineToolAliases})。
|
|
65
|
+
* **只读这一只真源**([ref])。本仓自身不声明带别名的工具;嵌入方经 `resolveSpec` 席挂的别名与 MCP 工具名在受理面看不见(名册是每任务装配的),**不在射程**。
|
|
66
|
+
* · **正名同在则不拒**:`正名|别名` / `List.*` 会命中正名,不是「静默不命中」,且正是 7.107.0 CHANGELOG 给的「滚动期两名都写」补偿形。
|
|
67
|
+
* 只拒「别名那一路永远打不中、而它的正名不在选中集里」—— 含 `Read|ListPeers`(7.112.0 已拒;主会话裁:规则形采「按条目逐条问」,不放宽;卡面整 matcher「匹配不到任何现役名」形会把它放回 200)。
|
|
68
|
+
* 多拒(相对卡面形)三例与出路:`Read|ListPeers` / `ListPeers|mcp__.*`(别名支永不命中,出路:改成正名)/ 排除式 `^(?!ListAgents).*`(出路:`^(?!ListAgents|ListPeers).*` 两名都不命中 ⇒ 放)。
|
|
69
|
+
* · **空白与大小写(S-995 / sema-test [ref] 起,运行期与 CC 2.1.280 同形)**:字面 / 名字表读法({@link matcherLiteralNames};`|` 与 `,` 分隔)容忍两端与分隔符两侧空白 ⇒ `" ListPeers"` / `"ListPeers "` / `"Read | ListPeers"` 会命中别名 ⇒ 落进本规则 400;
|
|
70
|
+
* **区分大小写、不剥词内空白** ⇒ `"listpeers"` / `"List Peers"` 对别名与正名都选不中,照旧受理(CC 同形)。
|
|
71
|
+
* · **不在本规则内**:两边都选不中的 matcher(大小写错 / 词内空白 / 纯错字;目录之外的 MCP / 嵌入方工具名同样「选不中目录里任何名」,受理面无从区分错字与它们)=
|
|
72
|
+
* **「选不到目录里任何名」另一类,候设计(台账另立号,主会话裁)**。
|
|
73
|
+
*
|
|
74
|
+
* 🔴 **「未判」臂过 `recordFailOpen`**(F 类 tag `MATCHER_UNJUDGED_TAG`;主会话裁,CLAUDE.md「合法 F 类回退走登记 tag」):一只 matcher 没能被判 ⇒ 照旧受理 + 登记一笔,
|
|
75
|
+
* detail = `event=… reason=… matcherChars=…`(**不带 matcher 原字节**,S-716 姿势)。三种 reason 同一个 tag:
|
|
76
|
+
* `oversize` —— 两档:matcher 超 {@link MAX_MATCHER_EVAL_CHARS} 字符不读(多租户车道上 hooks 不装配,这一笔只为让超长输入显形);
|
|
77
|
+
* `over_budget` ——「regex」档正则形:一次受理共享一份 {@link ADMISSION_MATCHER_EVAL_BUDGET_MS} 墙钟预算,用尽 ⇒ 这一只与其后各只正则形未判(`vm` 的 `timeout` 打断回溯,含 tier-up 之后的原生码);
|
|
78
|
+
* `invalid_regex` ——「regex」档正则形编不过(运行期另有每次调用的 `hook_matcher_invalid`;这是确定性的坏配置,不是负载 —— 拒它是新检测臂,不在本规则)。
|
|
79
|
+
* 放行最坏后果 = 那一只 matcher 没被这条判据守住、照旧永不命中,**不是安全闸**(受理面有效性判);反方向「越界即拒」会把负载抖动 / 病态但合法的 matcher 变成对合法请求的 400。
|
|
80
|
+
* 拒放集合相对 7.113.0 的一处放宽(披露在 CHANGELOG / MIGRATION):超过上限的字面别名 matcher 7.113.0 拒(当时字面判不设上限),自 7.114.0 两档都判未判、受理。
|
|
81
|
+
*
|
|
82
|
+
* 错误句只回显**目录里的**别名与正名(闭集字符串),不回显调用方的原字节(S-716 姿势)。
|
|
83
|
+
*
|
|
84
|
+
* 🔴 **判决是数据,句子与体的机读位同出一只铸点**(S-1025;client-core 请托,additive):{@link deadToolMatcherIssue} 回 {@link DeadToolMatcherIssue}(event / alias / canonical)
|
|
85
|
+
* 而不是句子;{@link buildDeadToolMatcherRefusal} 是**唯一**把它铸成拒句 + `field` + `detail` 的函数,受理门只取它的返回、**不解析句子**。体上多出的两键让三端不必解析英文就能把这只拒
|
|
86
|
+
* 与普通提交失败分流、提示用户改 matcher;句子逐字同 7.114.0、零新码。
|
|
41
87
|
*/
|
|
42
|
-
|
|
88
|
+
/** 超此长度的 matcher 不在受理面读(两档同一个上限;未判,reason = oversize)。与 {@link MAX_HOOK_COMMAND_CHARS} 同量纲的服务侧防线:真实 matcher(精确名 / 交替 / `mcp__.*`)远在其内;目录全部工具名交替 ~0.5 KiB。 */
|
|
89
|
+
export declare const MAX_MATCHER_EVAL_CHARS = 8192;
|
|
90
|
+
/** 一次 fresh 受理里 regex 档**正则形**求值共享的墙钟预算(毫秒)。字面形不占它。良性求值 ~0.1 ms;灾难回溯的 matcher 把预算吃完就停,后面的正则形组直接「未判」。 */
|
|
91
|
+
export declare const ADMISSION_MATCHER_EVAL_BUDGET_MS = 100;
|
|
92
|
+
/** 受理面判据的求值深度(见上头注):`"regex"` = 字面形按名字表答、正则形问运行期同一只正则求值(带预算);`"literal"` = 7.112.0 的字面判(零正则,正则形不适用)。 */
|
|
93
|
+
export type MatcherEvalDepth = "regex" | "literal";
|
|
94
|
+
/**
|
|
95
|
+
* **仅供测试注入**的求值预算(生产调用点不传 ⇒ 预算 = {@link ADMISSION_MATCHER_EVAL_BUDGET_MS}、时钟 = `performance.now`)。
|
|
96
|
+
* 让「预算是一次受理共享的」「耗尽之后的组」这类格子按求值次序断言,不按机器速度断言(`test/hooks-alias-matcher-s995.test.ts` R4)。
|
|
97
|
+
*/
|
|
98
|
+
export interface MatcherEvalBudget {
|
|
99
|
+
readonly budgetMs?: number;
|
|
100
|
+
readonly now?: () => number;
|
|
101
|
+
}
|
|
102
|
+
/** 一条受理面判决(纯数据):`event` 事件下的某只 matcher 选中了退役别名 `alias`、却选不中它的正名 `canonical`。`alias` / `canonical` 恒取自 core 静态目录(闭集字符串,不含调用方原字节)。 */
|
|
103
|
+
export interface DeadToolMatcherIssue {
|
|
104
|
+
readonly event: HookEventName;
|
|
105
|
+
readonly alias: string;
|
|
106
|
+
readonly canonical: string;
|
|
107
|
+
}
|
|
108
|
+
/** {@link buildDeadToolMatcherRefusal} 的返回(纯数据):拒句 + 400 体上的两个机读位。`field` 恒为字面 `"settings.hooks"`;`detail` 与 {@link DeadToolMatcherIssue} 同三键(逐个挑出,不 spread)。 */
|
|
109
|
+
export interface DeadToolMatcherRefusal {
|
|
110
|
+
readonly message: string;
|
|
111
|
+
readonly field: "settings.hooks";
|
|
112
|
+
readonly detail: {
|
|
113
|
+
readonly event: HookEventName;
|
|
114
|
+
readonly alias: string;
|
|
115
|
+
readonly canonical: string;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 判决 → 拒句 + 机读位。**拒句的唯一铸点**:句子逐字 = 7.114.0 的那一句(`test/hooks-alias-matcher-s995.test.ts` R7 冻结);受理门 `admission.ts` 用返回值填 400 体,
|
|
120
|
+
* 不从句子里反解字段。
|
|
121
|
+
*/
|
|
122
|
+
export declare function buildDeadToolMatcherRefusal(issue: DeadToolMatcherIssue): DeadToolMatcherRefusal;
|
|
123
|
+
/**
|
|
124
|
+
* fresh 提交面专用判据(S-924 / S-995):配置里第一条「永远打不中」的工具事件 matcher 的判决数据({@link DeadToolMatcherIssue});没有 ⇒ undefined。
|
|
125
|
+
* 拒句与 400 体的机读位由 {@link buildDeadToolMatcherRefusal} 从这份数据铸(S-1025)。
|
|
126
|
+
* 入参是 {@link parseHooksConfig} 已校验过的配置;`depth` 由调用点按车道定(见头注:`taskHooksHonored(config) ? "regex" : "literal"`)。
|
|
127
|
+
* `budget` **仅供测试注入**({@link MatcherEvalBudget});生产调用点只传前两个实参。
|
|
128
|
+
* **只有 `admission.ts` 的 fresh 提交门调它** —— resume 重放不调(理由见上面的头注)。未判的 matcher 过 `recordFailOpen`(tag `MATCHER_UNJUDGED_TAG`)。
|
|
129
|
+
*/
|
|
130
|
+
export declare function deadToolMatcherIssue(config: HooksConfig, depth: MatcherEvalDepth, budget?: MatcherEvalBudget): DeadToolMatcherIssue | undefined;
|
|
43
131
|
/**
|
|
44
132
|
* 把 wire 上的 untrusted `settings.hooks` 校验成契约 {@link HooksConfig} + 服务侧上限。
|
|
45
133
|
* 错误返回 error 字符串而不 throw——submit 路径 400 fail-loud、resume 防御路径 warn-drop,由调用方选。
|
|
46
134
|
*/
|
|
47
135
|
export declare function parseHooksConfig(raw: unknown): ParseHooksConfigResult;
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
136
|
+
/**
|
|
137
|
+
* matcher 的**字面 / 名字表读法**(S-995 / sema-test [ref];与 CC 2.1.280 同形):matcher 只含名字字符(`[A-Za-z0-9_-]`)、竖线、**逗号**与**空格**时,它不是正则而是「名字列表」——
|
|
138
|
+
* 按 `[|,]` 拆、**每段 trim**、丢空段;返回这张名字表。含任何别的字符(`.` / `*` / 括号 / 制表 / 换行 …)⇒ `undefined` = 不是字面读法,整体当正则(由调用方处置,**一字不动**)。
|
|
139
|
+
* 大小写敏感、词内空白不剥(`"Ba sh"` 是一个词 `Ba sh`)。**对所有带 matcher 的事件同一只读法**(工具事件 / StopFailure 的 `error` 词 / PreCompact·PostCompact 的 `trigger` 词 /
|
|
140
|
+
* `if` 条件的名部),不按事件分档。
|
|
141
|
+
*
|
|
142
|
+
* 等价性的依据(收车档 ⑥ / ⑦):
|
|
143
|
+
* · 黑盒 —— sema-test `repro/cc-r219-mcpstar-hookmatcher-20261003/`([ref],CC 2.1.280 真 TUI + 真 command 钩子):`" Bash"` / `"Bash "` / `" Bash "` / `"Read | Bash"` 命中 3/3,
|
|
144
|
+
* `"Ba sh"`(词内空白)/ `bash` / `BASH` 不命中(零告警),`^Bash$` / `Bash.*` / `(Bash)` / `Read|Bash` 照命中;PreToolUse 与 PostToolUse 同向。**逗号没有黑盒读数**,只有语料。
|
|
145
|
+
* · 语料 —— `sema-internal/blueprints/cc-decoded/cli280.js:216638` `uAe(matchValue, matcher, flag, …)` → `$mr(matcher, flag, …)`:`flag` 真时字符集门 `/^[a-zA-Z0-9_|, -]+$/` 过了才
|
|
146
|
+
* `matcher.split(/[|,]/).map(trim).filter(Boolean)` 做成员判;过不了才 `new RegExp(matcher)`(CC 的正则路径**不锚定**,本仓锚定 `^(?:…)$` —— 既有差异,不动)。
|
|
147
|
+
* 本函数的字符集与拆符与 CC 逐字同(空白只认空格 0x20)。
|
|
148
|
+
* · 已知差异(如实登记,本车不做):CC 的空白 / 逗号放行**按事件分档**(`dAe` 集合:工具事件 / PreCompact / PostCompact 等放,StopFailure 不放),本函数不分事件(第二批裁:同一只读法);
|
|
149
|
+
* CC 正则路径不锚定、本仓锚定。两条登记在 CHANGELOG 7.114.0 线 · 车 UW 已知边界。
|
|
150
|
+
* 零正则:单趟逐字符扫描(7.114.0 修复轮起;只为非空段分配名字,不按分隔符个数分配)。受理面两档({@link deadToolMatcherIssue})与运行期求值**共用这一只**读法,且两档都先走它、不占求值预算 ——
|
|
151
|
+
* 两档在字面子集上答案逐位相同是**构造出来的**,不是对拍出来的(超过长度上限的 matcher 两档都不读、同判未判)。
|
|
152
|
+
*/
|
|
153
|
+
export declare function matcherLiteralNames(matcher: string): readonly string[] | undefined;
|
|
154
|
+
/** CC matcher 语义:空/`*`/缺省匹配一切;**字面 / 名字表读法**({@link matcherLiteralNames}:`|` 与 `,` 分隔、每段 trim、不剥词内空白、区分大小写)按名字表成员判;
|
|
155
|
+
* 其余整体当 JS 正则({@link matcherPatternMatches};锚定全匹配;S-995 起正则路径一字不动);非法正则 → 该条不匹配(fail-closed 到"不跑",由调用方记账一次)。 */
|
|
50
156
|
export declare function hookMatcherMatches(matcher: string | undefined, value: string): boolean | "invalid";
|
|
51
157
|
/** `if` 条目条件(CC-exact,对照 CC 2.1.187/198 实测行为核验的真语义,两版一致):
|
|
52
158
|
* 值 = permission-rule 模式(`ToolName` 或 `ToolName(arg-模式)`),【不是】表达式语言——按当前工具调用
|