@sema-agent/client-core 0.62.1 → 0.63.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/CHANGELOG.md +135 -0
- package/README.md +9 -2
- package/dist/autoModeUnavailable.d.ts +77 -0
- package/dist/autoModeUnavailable.js +101 -0
- package/dist/engineIdentity.d.ts +94 -0
- package/dist/engineIdentity.js +143 -0
- package/dist/engineNoticeCodes.d.ts +91 -0
- package/dist/engineNoticeCodes.js +215 -0
- package/dist/gateVocabulary.d.ts +67 -0
- package/dist/gateVocabulary.js +134 -0
- package/dist/hitl/persistedRulesWire.d.ts +63 -1
- package/dist/hitl/persistedRulesWire.js +86 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +21 -0
- package/dist/liveInitToolFace.js +13 -0
- package/dist/permissionRuleIssue.d.ts +33 -0
- package/dist/permissionRuleIssue.js +121 -0
- package/dist/postureKnob.d.ts +90 -0
- package/dist/postureKnob.js +174 -0
- package/dist/steering.d.ts +6 -1
- package/dist/steering.js +10 -2
- package/dist/subagentContentStore.d.ts +5 -1
- package/dist/subagentContentStore.js +230 -90
- package/dist/toolRoster.d.ts +182 -0
- package/dist/toolRoster.js +236 -0
- package/docs/INTEGRATION-CLIENTS.md +503 -24
- package/package.json +4 -4
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **谁拒的** —— 判决是这次 deny 的那一**层**(sdk `DeniedBy` / core `DENIED_BY_VALUES`,
|
|
3
|
+
* 7.6.0 起八词、**7.9.0 起九词**)。顺序逐字同源。
|
|
4
|
+
* 🔴 真闭集(见模块顶注):表外词到不了消费端,所以这张表**没有**逃生口。
|
|
5
|
+
|
|
6
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
7
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
8
|
+
*/
|
|
9
|
+
export const GATE_DENIED_BY_WORDS = Object.freeze([
|
|
10
|
+
'policy',
|
|
11
|
+
'hook',
|
|
12
|
+
'org',
|
|
13
|
+
'persisted_rule',
|
|
14
|
+
'classifier',
|
|
15
|
+
'plan_mode',
|
|
16
|
+
'compliance',
|
|
17
|
+
'write_protection',
|
|
18
|
+
'ask_resolution',
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* 逐词一句人话。**九句刻意逐字互异**:对用户/运维是九条不同的下一步(改部署策略 / 改 hook /
|
|
22
|
+
* 找组织管理员 / 撤自己的常驻规则 / 调 auto 模式 / 退出 plan 模式 / 找合规 / 改写保护表 /
|
|
23
|
+
* 看那次审批的结算)。
|
|
24
|
+
*/
|
|
25
|
+
const DENIED_BY_SENTENCES = Object.freeze({
|
|
26
|
+
/** 部署 ToolPolicy 拒(直接拒,或复查一次已批准的编辑),或审批-编辑链撞了轮次上限。 */
|
|
27
|
+
policy: "denied by this deployment's permission policy",
|
|
28
|
+
/** PreToolUse hook 拒 / 抛 / 从未作答 —— 「没作决定的 hook」的 fail-closed 拦阻也按 hook 层归因。 */
|
|
29
|
+
hook: 'denied by a PreToolUse hook',
|
|
30
|
+
/** 组织策略规则拒。 */
|
|
31
|
+
org: 'denied by an organization rule',
|
|
32
|
+
/** **这个人自己**名下的一条常驻 deny 规则拒(他 settings 的 deny 表导进来的那类行)。
|
|
33
|
+
* 它是 `org` 的**个人店同胞**:同属「可以否掉一次人已经给过的批准」的复查层。 */
|
|
34
|
+
persisted_rule: 'denied by a persisted rule',
|
|
35
|
+
/** auto 模式分类器**直接**拒(它的 ASK 侧角色是 `origin: "denial_limit_fallback"`,不是这一格)。 */
|
|
36
|
+
classifier: 'denied by the auto-mode classifier',
|
|
37
|
+
/** plan 模式对写工具的只读拦阻。 */
|
|
38
|
+
plan_mode: 'denied because plan mode only allows reads',
|
|
39
|
+
/** 合规的调用期锁。 */
|
|
40
|
+
compliance: 'denied by a compliance lock',
|
|
41
|
+
/** 一次被批准的编辑被限制链改写到了**没有任何审批覆盖**的写保护路径上。 */
|
|
42
|
+
write_protection: 'denied because the write landed on a write-protected path',
|
|
43
|
+
/** 这次 ask 的**结算本身**就是拒(有人说了不 / 窗到期 / 没人可问……)—— 细节在 `settlement`。 */
|
|
44
|
+
ask_resolution: 'denied when the approval was resolved',
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* 一个 `deniedBy` 词 → 一句人话。**唯一铸点**(三端共用;端零自拼)。
|
|
48
|
+
*
|
|
49
|
+
* 🔴 表外词的那一句说的是「**这条门记录本不该长这样**」,不是「有个新词」——理由见模块顶注
|
|
50
|
+
* (出集的记录在 server 侧整条不上帧,所以真读到一个表外词意味着记录有缺陷)。原样带上那个
|
|
51
|
+
* 词:运维要拿它去问上游。
|
|
52
|
+
* 🔴 非串 / 空串同样走兜底 —— 一个读不出的层名不是「没有层」(deny 臂在类型上就必须点名一层)。
|
|
53
|
+
* 🔴 **查表用 `Object.hasOwn`,不用 `in` / 裸下标**(异源对抗复审 [medium]):`Object.freeze` 冻的是
|
|
54
|
+
* **自有属性**,原型链原样还在 ⇒ 一个来自 wire 的 `constructor` / `toString` / `__proto__` 会命中
|
|
55
|
+
* `Object.prototype` 上的成员,措辞口于是返回一个**函数**而不是一句话:呈现调用当场坏掉,而且
|
|
56
|
+
* 它绕过了上下两条精心分家的兜底。这是本仓已定谳的病形(wire 键控的表一律用 Map 或自有属性判据)。
|
|
57
|
+
*/
|
|
58
|
+
export function gateDeniedByDetail(deniedBy) {
|
|
59
|
+
if (typeof deniedBy === 'string' && Object.hasOwn(DENIED_BY_SENTENCES, deniedBy)) {
|
|
60
|
+
return DENIED_BY_SENTENCES[deniedBy];
|
|
61
|
+
}
|
|
62
|
+
const word = typeof deniedBy === 'string' && deniedBy.length > 0 ? deniedBy : '(none)';
|
|
63
|
+
return `denied, but the layer name ${word} is not one this gate record should be able to carry — engines withhold records that carry an unknown layer, so this one is damaged`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 一只 ask 的**出身** —— 谁问的(sdk `AskOrigin` / core `ASK_ORIGINS`;7.5.x 及以前八词、
|
|
67
|
+
* 7.6.0 十词、**7.9.0 起十一词**)。顺序逐字同源。
|
|
68
|
+
* 🔴 真开集(见模块顶注):server 只判非空串,新词会带着合法的帧到达。
|
|
69
|
+
|
|
70
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
71
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
72
|
+
*/
|
|
73
|
+
export const ASK_ORIGIN_WORDS = Object.freeze([
|
|
74
|
+
'content_question',
|
|
75
|
+
'unresolvable',
|
|
76
|
+
'org_unavailable',
|
|
77
|
+
'org_rule',
|
|
78
|
+
'rule_store_unavailable',
|
|
79
|
+
'hook',
|
|
80
|
+
'ask_rule',
|
|
81
|
+
'denial_limit_fallback',
|
|
82
|
+
'shell_gate_tighten',
|
|
83
|
+
'safety_tighten',
|
|
84
|
+
'policy',
|
|
85
|
+
]);
|
|
86
|
+
/**
|
|
87
|
+
* 逐词一句人话。**十一句刻意逐字互异** —— 尤其是这三对刻意分家的同胞:
|
|
88
|
+
* · `org_unavailable` / `rule_store_unavailable`:两个「治理源这次读不出来 ⇒ fail-closed 问人」,
|
|
89
|
+
* 一个是**组织**店、一个是**这个人自己**的持久规则店,下一步找的人不同;
|
|
90
|
+
* · `shell_gate_tighten` / `safety_tighten`:两条 tighten 分成两个词,正是为了说出**哪一层**
|
|
91
|
+
* 引擎逻辑提的问(粗粒度 shellGate 教条 vs 调用的显式事实:egress 标 / 不可逆标 / 写保护);
|
|
92
|
+
* · `org_rule` / `ask_rule`:组织的规则 vs 这个人自己的常驻 ask 行。
|
|
93
|
+
*/
|
|
94
|
+
const ASK_ORIGIN_SENTENCES = Object.freeze({
|
|
95
|
+
content_question: 'the tool itself asked you a question',
|
|
96
|
+
unresolvable: 'the gate could not decide on its own, so it asks',
|
|
97
|
+
org_unavailable: 'the organization policy was unavailable, so this call asks',
|
|
98
|
+
org_rule: 'an organization rule asks about this call',
|
|
99
|
+
rule_store_unavailable: 'the rule store was unavailable, so this call asks',
|
|
100
|
+
hook: 'a PreToolUse hook asked about this call',
|
|
101
|
+
ask_rule: 'a persisted ask rule matches this call',
|
|
102
|
+
denial_limit_fallback: 'the auto-mode classifier hit its denial limit and handed this call back to you',
|
|
103
|
+
shell_gate_tighten: 'this deployment asks about every shell command at this gate setting',
|
|
104
|
+
safety_tighten: 'the gate tightened on this call’s own facts (network egress, irreversibility, or a protected write)',
|
|
105
|
+
policy: 'this deployment’s permission policy asks about this call',
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* 一个 `origin` 词 → 一句人话。**唯一铸点**(三端共用;端零自拼)。
|
|
109
|
+
*
|
|
110
|
+
* 🔴 表外词的那一句说的是「**这个词比这一端新**」,不是「坏记录」——理由见模块顶注(server 只判
|
|
111
|
+
* 非空串,core 加词当天合法的帧就带着它到达)。原样带上那个词,并明说这次仍然是在问人:
|
|
112
|
+
* 读不懂出身**不改变**这只 ask 要人回答这件事。
|
|
113
|
+
* 🔴 非串 / 空串:同走兜底但词位渲 `(none)` —— 「没报出身」与「报了一个读不懂的出身」在这一句里
|
|
114
|
+
* 不必分家(两者对用户的下一步相同:照常回答这只 ask),但都**不许**被折成十一词里的任何一个。
|
|
115
|
+
*/
|
|
116
|
+
export function askOriginDetail(origin) {
|
|
117
|
+
// 🔴 自有属性判据,理由与 `gateDeniedByDetail` 逐字相同(冻结不移除原型)。
|
|
118
|
+
const known = typeof origin === 'string' && Object.hasOwn(ASK_ORIGIN_SENTENCES, origin)
|
|
119
|
+
? ASK_ORIGIN_SENTENCES[origin]
|
|
120
|
+
: undefined;
|
|
121
|
+
if (known !== undefined)
|
|
122
|
+
return known;
|
|
123
|
+
const word = typeof origin === 'string' && origin.length > 0 ? origin : '(none)';
|
|
124
|
+
return `this call asks; its origin ${word} is a word newer than this client, so answer it as usual`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* **编译期对账钉**(不出公面):本包两张表的成员必须真属于 sdk 的两个联合。
|
|
128
|
+
* 🔴 `DeniedBy` 那一张钉得**双向**(它在 wire 上是真闭集,型面也闭);`AskOrigin` 那一张只钉
|
|
129
|
+
* 单向(它有逃生口,任何字符串都合法 —— 钉反向等于把开集当闭集用)。
|
|
130
|
+
*/
|
|
131
|
+
const _deniedByWordsPin = GATE_DENIED_BY_WORDS;
|
|
132
|
+
void _deniedByWordsPin;
|
|
133
|
+
const _askOriginWordsPin = ASK_ORIGIN_WORDS;
|
|
134
|
+
void _askOriginWordsPin;
|
|
@@ -27,11 +27,22 @@
|
|
|
27
27
|
* 七类共用一格,wire 上分不出来是设计)。唯一可分的是 503 `state.rule_import_retry`:那一支意味着
|
|
28
28
|
* **票还在**,原样重试即可(与「这张票没了」是相反的处置)。
|
|
29
29
|
*
|
|
30
|
+
* ── 🔴 三态规则身份:`behavior` 必填且不给默认(server ≥7.67.0 / sdk 8.8.0 BREAKING)──────────
|
|
31
|
+
* 规则车道此前只有 allow,撤销体按 `(rule, scope)` 两元组寻址就够了。7.67.0 起 deny/ask 也进同一个
|
|
32
|
+
* 店 —— **同一段文本的 `deny` 与 `allow` 是两条不同的行**。一次瞄准 `allow` 的撤销若被默认成同文本
|
|
33
|
+
* 的 `deny`,删掉的是一条本该留着的**拒绝**规则,而调用方收到的是一个 200(引擎侧
|
|
34
|
+
* `sameRuleIdentity` 顶注逐字写着这一形)。⇒ 本模块两条纪律:
|
|
35
|
+
* · 读:{@link persistedRuleBehaviorOf} 读不出这一格就答 `undefined`,**绝不补 allow 默认**;
|
|
36
|
+
* 列举半场**照样把那一行交出去**(丢行 = 把一条活规则从治理清单里藏起来,正是
|
|
37
|
+
* {@link listAllPersistedRules} 拒绝做的事),呈现走 {@link persistedRuleBehaviorLabel};
|
|
38
|
+
* · 写:{@link revokeTargetFromPersistedRule} 读不出这一格就**拒铸撤销体** —— 猜一态的代价是
|
|
39
|
+
* 不可逆地删掉另一态的行,而拒铸的代价只是治理面上少一个按钮。
|
|
40
|
+
*
|
|
30
41
|
* ── UNTRUSTED ───────────────────────────────────────────────────────────────────────────────
|
|
31
42
|
* 规则文本与 `skipped[].reason` 都是引擎/用户 settings 侧的内容,只渲染绝不当代码用;`reason` 是
|
|
32
43
|
* **给人看的散文不是机读码**(SDK 头注),分类只许按第一个 `:` 前缀,并且要容得下**没有前缀**的形。
|
|
33
44
|
*/
|
|
34
|
-
import type { CcImportLayer, CcImportPrepareResult, CcImportRedeemResult, PersistedRule, RuleListParams, RuleListResult, RuleRevokeRequest, RuleRevokeResult } from '@sema-agent/sdk';
|
|
45
|
+
import type { CcImportLayer, CcImportPrepareResult, CcImportRedeemResult, PersistedRule, RuleBehavior, RuleListParams, RuleListResult, RuleRevokeRequest, RuleRevokeResult } from '@sema-agent/sdk';
|
|
35
46
|
/** 规则车道消费的 `client.rules` 切片(注入缝:宿主给真 SDK facade,测试给假件)。 */
|
|
36
47
|
export interface RulesFacade {
|
|
37
48
|
list(params?: RuleListParams, opts?: {
|
|
@@ -121,6 +132,57 @@ export type RulesFailure =
|
|
|
121
132
|
message: string;
|
|
122
133
|
};
|
|
123
134
|
export declare function classifyRulesFailure(e: unknown): RulesFailure;
|
|
135
|
+
/**
|
|
136
|
+
* 一条持久规则是哪一态 —— sdk `RuleBehavior` 的**运行期镜像**(引擎侧同一张闭三词表)。
|
|
137
|
+
* `allow` = 一次人工批准的常驻形(卡上点的「不再询问」);`deny` / `ask` = 「永不运行」与
|
|
138
|
+
* 「每次都问我」的常驻形。**一套文法、一种规范拼法、一族匹配器**服务三态,态是**文本旁边的
|
|
139
|
+
* 一格**,永远不是文本的一部分。
|
|
140
|
+
* 🔴 门 `run-rules-side-test.mjs` G9a 对 sdk 的联合成员**逐词等值**对账(缺一词红、多一词红)。
|
|
141
|
+
|
|
142
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
143
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
144
|
+
*/
|
|
145
|
+
export declare const PERSISTED_RULE_BEHAVIORS: readonly RuleBehavior[];
|
|
146
|
+
/**
|
|
147
|
+
* 这一行是哪一态。**闭三词**之外(含整键缺席的老 worker 行)一律 `undefined`。
|
|
148
|
+
*
|
|
149
|
+
* 🔴 **绝不补默认**:缺席是「这台 worker 说不出来」,不是 `allow`。补一个默认会让呈现面
|
|
150
|
+
* 对一条 deny 行说 allow,而撤销面据此瞄准另一态。
|
|
151
|
+
* 🔴 这里**按闭集读**(与本包其余开集读口刻意不同):这一格是**身份**的一格,不是一个展示词——
|
|
152
|
+
* 读不懂的身份必须判「读不出」,而不是原样当成一个能拿去撤销的态。呈现层要原样渲那个词,
|
|
153
|
+
* 走 {@link persistedRuleBehaviorLabel}(它是开集的)。
|
|
154
|
+
*/
|
|
155
|
+
export declare function persistedRuleBehaviorOf(rule: unknown): RuleBehavior | undefined;
|
|
156
|
+
/** 读不出态时呈现面渲的那个词。**逐字锚**(三端共用一句;端零自拼)。 */
|
|
157
|
+
export declare const PERSISTED_RULE_BEHAVIOR_UNKNOWN = "(unknown)";
|
|
158
|
+
/**
|
|
159
|
+
* 治理行上「态」那一列的**唯一措辞铸点**。
|
|
160
|
+
* 🔴 三态渲词本身;**表外词原样渲**(开集 —— 引擎加词那天治理面照样看得见那个词,不至于把一条
|
|
161
|
+
* 真实的行渲成「读不出」);**整键缺席/坏形**才渲 {@link PERSISTED_RULE_BEHAVIOR_UNKNOWN} ——
|
|
162
|
+
* 它不冒充三态里的任何一个,读到它的人知道下一步是升级那台 worker。
|
|
163
|
+
*/
|
|
164
|
+
export declare function persistedRuleBehaviorLabel(rule: unknown): string;
|
|
165
|
+
/**
|
|
166
|
+
* 一条列举行 → 撤销体(`rules.revoke()` 的入参)。**按内容寻址**,这条面上下没有任何 id。
|
|
167
|
+
*
|
|
168
|
+
* 🔴 **身份三元组逐字回传**(`behavior` / `rule` / `scope`):拼法等价但不逐字相同的 scope 什么都
|
|
169
|
+
* 匹配不上,而且会得到一个笑呵呵的 `no-op`;所以原样送回引擎给的那串字节,别拿解析后的形重铸。
|
|
170
|
+
* 🔴 **只带身份三键 + 可选 `principal`**:`source` / `status` 是**派生键**,回传它们会被拒。
|
|
171
|
+
* 🔴 **`behavior` 读不出 ⇒ 拒铸(返回 `undefined`)**,这正是本次 BREAKING 的全部理由:猜一态的
|
|
172
|
+
* 代价是不可逆地删掉另一态的行且调用方收到 200;拒铸的代价只是治理面少一个按钮。调用方据此
|
|
173
|
+
* 藏掉撤销入口(并可用 {@link persistedRuleBehaviorLabel} 告诉用户为什么)。
|
|
174
|
+
* 🔴 `rule` / `scope` 任一读不出也拒铸:按 `undefined` 撤销什么都对不上。
|
|
175
|
+
* 🔴 **显式给了一个无效 `principal` ≠ 没给**(异源对抗复审 [high]):`principal` **缺席**明确表示
|
|
176
|
+
* 「撤**我自己的**」。若把一个显式传进来的空串/非串静默降级成缺席,一次瞄准**别人**的 operator
|
|
177
|
+
* 撤销就会转向**调用者自己**的规则桶,并且拿到一个 200 —— 而目标值算空在治理面上是常见形
|
|
178
|
+
* (表单空字段、一次没查到的用户名)。⇒ 键**在场但值不合法**时**拒铸整只**,与 `behavior`
|
|
179
|
+
* 读不出同一条处置。`opts` 整只不给、或给了但不含这个键,才是「缺席 = 撤我自己的」那个合法意图。
|
|
180
|
+
* ⚠️ 合法值**原样送出不 trim**:principal 的规范形归引擎,壳替它 trim 会让两端对「同一个人」的
|
|
181
|
+
* 拼法各有一份判据。
|
|
182
|
+
*/
|
|
183
|
+
export declare function revokeTargetFromPersistedRule(rule: unknown, opts?: {
|
|
184
|
+
principal?: string;
|
|
185
|
+
}): RuleRevokeRequest | undefined;
|
|
124
186
|
/** 一次「列全」的结果。`rules` 恒是**完整**清单(翻不完 ⇒ 走 failure,绝不交半份清单)。 */
|
|
125
187
|
export type ListAllPersistedRulesOutcome = {
|
|
126
188
|
ok: true;
|
|
@@ -89,6 +89,92 @@ export function classifyRulesFailure(e) {
|
|
|
89
89
|
return { kind: 'forbidden', message };
|
|
90
90
|
return { kind: 'error', message };
|
|
91
91
|
}
|
|
92
|
+
// ── 三态规则身份(behavior)────────────────────────────────────────────────────────────────────
|
|
93
|
+
/**
|
|
94
|
+
* 一条持久规则是哪一态 —— sdk `RuleBehavior` 的**运行期镜像**(引擎侧同一张闭三词表)。
|
|
95
|
+
* `allow` = 一次人工批准的常驻形(卡上点的「不再询问」);`deny` / `ask` = 「永不运行」与
|
|
96
|
+
* 「每次都问我」的常驻形。**一套文法、一种规范拼法、一族匹配器**服务三态,态是**文本旁边的
|
|
97
|
+
* 一格**,永远不是文本的一部分。
|
|
98
|
+
* 🔴 门 `run-rules-side-test.mjs` G9a 对 sdk 的联合成员**逐词等值**对账(缺一词红、多一词红)。
|
|
99
|
+
|
|
100
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
101
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
102
|
+
*/
|
|
103
|
+
export const PERSISTED_RULE_BEHAVIORS = Object.freeze(['deny', 'ask', 'allow']);
|
|
104
|
+
const BEHAVIOR_SET = new Set(PERSISTED_RULE_BEHAVIORS);
|
|
105
|
+
/** 一行上非空字符串字段的取值(其余一律 `undefined`)。 */
|
|
106
|
+
function rowStr(row, key) {
|
|
107
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
108
|
+
return undefined;
|
|
109
|
+
const v = row[key];
|
|
110
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 这一行是哪一态。**闭三词**之外(含整键缺席的老 worker 行)一律 `undefined`。
|
|
114
|
+
*
|
|
115
|
+
* 🔴 **绝不补默认**:缺席是「这台 worker 说不出来」,不是 `allow`。补一个默认会让呈现面
|
|
116
|
+
* 对一条 deny 行说 allow,而撤销面据此瞄准另一态。
|
|
117
|
+
* 🔴 这里**按闭集读**(与本包其余开集读口刻意不同):这一格是**身份**的一格,不是一个展示词——
|
|
118
|
+
* 读不懂的身份必须判「读不出」,而不是原样当成一个能拿去撤销的态。呈现层要原样渲那个词,
|
|
119
|
+
* 走 {@link persistedRuleBehaviorLabel}(它是开集的)。
|
|
120
|
+
*/
|
|
121
|
+
export function persistedRuleBehaviorOf(rule) {
|
|
122
|
+
const b = rowStr(rule, 'behavior');
|
|
123
|
+
return b !== undefined && BEHAVIOR_SET.has(b) ? b : undefined;
|
|
124
|
+
}
|
|
125
|
+
/** 读不出态时呈现面渲的那个词。**逐字锚**(三端共用一句;端零自拼)。 */
|
|
126
|
+
export const PERSISTED_RULE_BEHAVIOR_UNKNOWN = '(unknown)';
|
|
127
|
+
/**
|
|
128
|
+
* 治理行上「态」那一列的**唯一措辞铸点**。
|
|
129
|
+
* 🔴 三态渲词本身;**表外词原样渲**(开集 —— 引擎加词那天治理面照样看得见那个词,不至于把一条
|
|
130
|
+
* 真实的行渲成「读不出」);**整键缺席/坏形**才渲 {@link PERSISTED_RULE_BEHAVIOR_UNKNOWN} ——
|
|
131
|
+
* 它不冒充三态里的任何一个,读到它的人知道下一步是升级那台 worker。
|
|
132
|
+
*/
|
|
133
|
+
export function persistedRuleBehaviorLabel(rule) {
|
|
134
|
+
return rowStr(rule, 'behavior') ?? PERSISTED_RULE_BEHAVIOR_UNKNOWN;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 一条列举行 → 撤销体(`rules.revoke()` 的入参)。**按内容寻址**,这条面上下没有任何 id。
|
|
138
|
+
*
|
|
139
|
+
* 🔴 **身份三元组逐字回传**(`behavior` / `rule` / `scope`):拼法等价但不逐字相同的 scope 什么都
|
|
140
|
+
* 匹配不上,而且会得到一个笑呵呵的 `no-op`;所以原样送回引擎给的那串字节,别拿解析后的形重铸。
|
|
141
|
+
* 🔴 **只带身份三键 + 可选 `principal`**:`source` / `status` 是**派生键**,回传它们会被拒。
|
|
142
|
+
* 🔴 **`behavior` 读不出 ⇒ 拒铸(返回 `undefined`)**,这正是本次 BREAKING 的全部理由:猜一态的
|
|
143
|
+
* 代价是不可逆地删掉另一态的行且调用方收到 200;拒铸的代价只是治理面少一个按钮。调用方据此
|
|
144
|
+
* 藏掉撤销入口(并可用 {@link persistedRuleBehaviorLabel} 告诉用户为什么)。
|
|
145
|
+
* 🔴 `rule` / `scope` 任一读不出也拒铸:按 `undefined` 撤销什么都对不上。
|
|
146
|
+
* 🔴 **显式给了一个无效 `principal` ≠ 没给**(异源对抗复审 [high]):`principal` **缺席**明确表示
|
|
147
|
+
* 「撤**我自己的**」。若把一个显式传进来的空串/非串静默降级成缺席,一次瞄准**别人**的 operator
|
|
148
|
+
* 撤销就会转向**调用者自己**的规则桶,并且拿到一个 200 —— 而目标值算空在治理面上是常见形
|
|
149
|
+
* (表单空字段、一次没查到的用户名)。⇒ 键**在场但值不合法**时**拒铸整只**,与 `behavior`
|
|
150
|
+
* 读不出同一条处置。`opts` 整只不给、或给了但不含这个键,才是「缺席 = 撤我自己的」那个合法意图。
|
|
151
|
+
* ⚠️ 合法值**原样送出不 trim**:principal 的规范形归引擎,壳替它 trim 会让两端对「同一个人」的
|
|
152
|
+
* 拼法各有一份判据。
|
|
153
|
+
*/
|
|
154
|
+
export function revokeTargetFromPersistedRule(rule, opts) {
|
|
155
|
+
const behavior = persistedRuleBehaviorOf(rule);
|
|
156
|
+
if (behavior === undefined)
|
|
157
|
+
return undefined;
|
|
158
|
+
const text = rowStr(rule, 'rule');
|
|
159
|
+
const scope = rowStr(rule, 'scope');
|
|
160
|
+
if (text === undefined || scope === undefined)
|
|
161
|
+
return undefined;
|
|
162
|
+
let principal;
|
|
163
|
+
if (opts !== null && typeof opts === 'object' && 'principal' in opts) {
|
|
164
|
+
const raw = opts.principal;
|
|
165
|
+
// 键在场 ⇒ 它必须是一个**能指人**的名字;空白串指不了任何人,而「指不了人」在这条面上
|
|
166
|
+
// 会被引擎读成「就是调用者自己」——那正是本闸要挡的静默改写。
|
|
167
|
+
if (typeof raw !== 'string' || raw.trim().length === 0)
|
|
168
|
+
return undefined;
|
|
169
|
+
principal = raw;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
behavior,
|
|
173
|
+
rule: text,
|
|
174
|
+
scope,
|
|
175
|
+
...(principal !== undefined ? { principal } : {}),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
92
178
|
/** 一页要多少条。**必须显式给**(对抗复审 [medium] 实撞):server 缺省是 **50**,而页帽
|
|
93
179
|
* 按「200/页」算 ⇒ 真实上界只有 1250 条,一位规则多于 1250 的 principal 会恒拿到「翻不完」的
|
|
94
180
|
* 失败、整个治理面打不开,而注释还写着 5000。夹取语义在 server(非数/越界夹进 1..200),所以给
|
package/dist/index.d.ts
CHANGED
|
@@ -153,6 +153,13 @@ export * from './writeProtectionCapability.js';
|
|
|
153
153
|
export * from './runTerminal.js';
|
|
154
154
|
export * from './readFacePosture.js';
|
|
155
155
|
export * from './gateOutcome.js';
|
|
156
|
+
export * from './postureKnob.js';
|
|
157
|
+
export * from './engineIdentity.js';
|
|
158
|
+
export * from './gateVocabulary.js';
|
|
159
|
+
export * from './permissionRuleIssue.js';
|
|
160
|
+
export * from './toolRoster.js';
|
|
161
|
+
export * from './engineNoticeCodes.js';
|
|
162
|
+
export * from './autoModeUnavailable.js';
|
|
156
163
|
export * from './engineToolLabelStore.js';
|
|
157
164
|
export * from './fleetTaskDesc.js';
|
|
158
165
|
export type * from './types/engineState.js';
|
package/dist/index.js
CHANGED
|
@@ -174,6 +174,27 @@ export * from './runTerminal.js';
|
|
|
174
174
|
// 只带一个纯比较函数,渲染归端。
|
|
175
175
|
export * from './readFacePosture.js';
|
|
176
176
|
export * from './gateOutcome.js';
|
|
177
|
+
// 0.63.0(sdk 8.8.0 / engine ≥7.67.0 / S-178):`serverGates` 三根 posture 旋钮的读数窄读器
|
|
178
|
+
// (值 + 谁定的 + 指路句)。四词来源表在本包只有这一份,`readFacePosture` 与它共用。
|
|
179
|
+
export * from './postureKnob.js';
|
|
180
|
+
// 0.63.0(sdk 8.8.0 / engine ≥7.67.0 / S-179):`/health` 三只代际锚(pid / instanceId /
|
|
181
|
+
// startedAt)的窄读器与纯比较。判「换代之后做什么」是宿主的状态机,本包只给读与比。
|
|
182
|
+
export * from './engineIdentity.js';
|
|
183
|
+
// 0.63.0(sdk 8.8.0 / core 7.9.0 / L-162 ③):门词汇两张表(DeniedBy 九词「谁拒的」/
|
|
184
|
+
// AskOrigin 十一词「谁问的」)的唯一措辞铸点。两张表的开闭各按其出处,兜底句逐字分家。
|
|
185
|
+
export * from './gateVocabulary.js';
|
|
186
|
+
// 0.63.0(L-162 ②;core 7.9.x):权限规则 lint 的拒因码表 + 唯一措辞铸点。sdk 尚未导出这张表,
|
|
187
|
+
// 故按引擎真字节铸,门对实装 core 的两份产物双向对账;候上游导出即整只退役改成从 sdk 取。
|
|
188
|
+
export * from './permissionRuleIssue.js';
|
|
189
|
+
// 0.63.0(L-161;engine ≥7.66.0 有臂 / ≥7.9.0 真推):一条腿的工具名册投影 + tool_roster_delta
|
|
190
|
+
// 应用。名册在场用名册、缺席才回落 liveInitToolFace 那三张估计词表(退役条款登记在那个文件的头注)。
|
|
191
|
+
export * from './toolRoster.js';
|
|
192
|
+
// 0.63.0(L-167;core 7.9.x):engine_notice 码册与 audience 表的镜像 + mcp.injection_dropped
|
|
193
|
+
// 事实窄读器。措辞**不镜像**(上游立了 single_mint 契约:core 铸句子,消费端用它给的 message)。
|
|
194
|
+
export * from './engineNoticeCodes.js';
|
|
195
|
+
// 0.63.0 件⑧(core 7.10.0 #616):「这只 ask 是因为分类器跑不了才问人」的事实读器 + 措辞铸点。
|
|
196
|
+
// 两条 cause 轴刻意不合并(不可用 / 熔断),`parse_error` 只在熔断轴上 —— 读器按不可用轴收窄。
|
|
197
|
+
export * from './autoModeUnavailable.js';
|
|
177
198
|
export * from './engineToolLabelStore.js';
|
|
178
199
|
export * from './fleetTaskDesc.js';
|
|
179
200
|
// ── B2 批:通知族合并 / caps-wire 门族 / 模型面纯逻辑 / control 路由(2026-07-27)──────────────
|
package/dist/liveInitToolFace.js
CHANGED
|
@@ -52,6 +52,19 @@ import { hostLog } from './host.js';
|
|
|
52
52
|
import { scenarioForPrint } from './scenarioWire.js';
|
|
53
53
|
import { resolveHeadlessInteractiveTools } from './interactiveToolsWire.js';
|
|
54
54
|
import { webSearchFromEnv } from './webSearchWireCaps.js';
|
|
55
|
+
// ── 🔴 退役条款(0.63.0 / L-161 登记;engine ≥7.66.0 有名册,≥7.9.0 才真推)──────────────────
|
|
56
|
+
// 下面三张表(`ENGINE_HANDS_BAND` / `ENGINE_SCENARIO_EXTRAS_DEFAULT` / `ENGINE_RUNNER_FACE`)是
|
|
57
|
+
// **估计值**:2026-07-16 一次 tap 实测按当时 pin 的引擎版本定稿,外接别的引擎、别的场景、别的
|
|
58
|
+
// env 门就会偏(文件头「已知局限」段自己写着这一条)。它们的继任者是引擎**自己说的**那份东西 ——
|
|
59
|
+
// `wiring_manifest.tools` 上的工具名册(本包 `toolRoster.ts`:`projectToolRoster` /
|
|
60
|
+
// `toolRosterNames` / `toolShimFromRoster`),那里有每只工具的身份、轴与面,不用按名字猜。
|
|
61
|
+
//
|
|
62
|
+
// **本批刻意不删表**,因为回落还得留着:名册只在 **effective 半场**(live 帧)铸,而本函数跑在
|
|
63
|
+
// 首帧之前走的是 HTTP 探测面;且只有 ≥7.9.0 的引擎才真推那条帧。⇒ 现役姿势 =
|
|
64
|
+
// **名册在场用名册,缺席才回落这三张表**,装配在宿主(它才拿得到帧流)。
|
|
65
|
+
// 退役条件(满足即删这三张表 + 删掉本段):**名册恒在场**的引擎版本成为支持底线。
|
|
66
|
+
// ⚠️ 删表时连 `runnerFaceForEnv` 的 env 门一起删 —— 那道门也是在补估计值的偏,名册在场时它是
|
|
67
|
+
// 第二份台账(引擎已经按 env 门算过一遍才铸的名册)。
|
|
55
68
|
// ── 壳已知 wire 词表(tap 实测 2026-07-16,server 1.214.0/core 1.300.0,壳 -p 缺省旋钮)──────────
|
|
56
69
|
/** hands band(本地/远程执行底座在位时 prepare-task 挂载;壳自 spawn 恒 REMOTE_EXEC=host ⇒ 在位)。 */
|
|
57
70
|
export const ENGINE_HANDS_BAND = [
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 引擎**真会铸**的规则 lint 拒因码(core `PermissionRuleIssue.code`,7.9.x = 十五码)。
|
|
3
|
+
* 顺序与 core 声明同源。
|
|
4
|
+
* 🔴 这是一份**抄件**,不是本包的意见 —— 改它必须同 commit 附 core 坐标,且门会先红。
|
|
5
|
+
|
|
6
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
7
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
8
|
+
*/
|
|
9
|
+
export declare const PERMISSION_RULE_ISSUE_CODES: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* 在 core 的**声明**联合里还留着、但引擎**再也不铸**的码(记账用,不进呈现表)。
|
|
12
|
+
* · `unsupported.unknown_param` —— core 7.8.0(B15)改名成 `unsupported.param_unknown`,
|
|
13
|
+
* 联合里的旧名没删干净。
|
|
14
|
+
* 🔴 这张表**不是**给消费端查的,它是给门的:`表 ∪ 退役表 == 声明联合` 这条等式让「上游删掉那一员」
|
|
15
|
+
* 与「上游又留下一个新的 stale 成员」都当场红。查码一律查 {@link PERMISSION_RULE_ISSUE_CODES}。
|
|
16
|
+
* ⚠️ 另有一个更早的退役名 `unsupported.bash_prefix_rules_store`(sdk README 记的减员)——它在 core
|
|
17
|
+
* 7.9.x 的**声明里已经没有了**,所以不登记在这里(登记一个两个真源都不认的名字,门会红)。
|
|
18
|
+
|
|
19
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
20
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
21
|
+
*/
|
|
22
|
+
export declare const RETIRED_PERMISSION_RULE_ISSUE_CODES: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* 一个 lint 拒因 → 一句人话(+ 引擎自己那句更具体的散文)。**唯一措辞铸点**(三端共用;端零自拼)。
|
|
25
|
+
*
|
|
26
|
+
* 🔴 表外码的那一句说的是「**这个码比这一端新**」——码表的属主是引擎,加码不该被渲成故障;
|
|
27
|
+
* 原样带上那个码(运维要拿它去问上游),并**照样带上 `message`**:码读不懂不代表引擎那句话
|
|
28
|
+
* 读不懂,而那句话往往才是能动手改的那条信息。
|
|
29
|
+
* 🔴 `message` 是**散文不是码**:只渲染、呈前消毒 + 封长,绝不做等值匹配、绝不当分类位。
|
|
30
|
+
* 🔴 坏码 / 坏 message 一律不抛 —— 这条路径跑在「用户规则写错了」的那一刻,读器自己再抛一次
|
|
31
|
+
* 只会把一个可以说清的错误换成一个说不清的崩。
|
|
32
|
+
*/
|
|
33
|
+
export declare function permissionRuleIssueDetail(code: unknown, message: unknown): string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/permissionRuleIssue.ts — 权限规则 lint 的**拒因码表 + 唯一措辞铸点**
|
|
3
|
+
* (0.63.0;L-162 ②;engine >=7.67.0 / core 7.9.x)。
|
|
4
|
+
*
|
|
5
|
+
* -- 这张表为什么是**铸**的而不是**取**的(以及它什么时候该被换掉)---------------------------
|
|
6
|
+
* 引擎在装配一份权限规则表时,对每条它不接的规则出一个 `{rule, code, message}`。`code` 是**机读
|
|
7
|
+
* 的处置分类**,`message` 是给人看的**散文**。sdk 8.8.0 **既无 spec schema 也无导出型**(sdk 自己的
|
|
8
|
+
* README 在「如实登记(本版没做的)」一节里写了这一格),所以本包没有可以 `import` 的上游码表。
|
|
9
|
+
* => 按引擎真字节铸一张。
|
|
10
|
+
*
|
|
11
|
+
* 🔴 **候上游导出即换**:sdk 哪天导出 `PermissionRuleIssue` 的码表(型或 `as const` 值表),本表
|
|
12
|
+
* 应当**整只退役**、改成从 sdk 取(与本包对 `RULE_OFFER_MATCHES` 那几张表的处置同形)。
|
|
13
|
+
* 给下游的反漂移锚:`dts-pattern` 盯 sdk 的 `PermissionRuleIssue` 上出现 `code` 声明。
|
|
14
|
+
* 🔴 **镜像的代价用门付,不用「记得同步」付**:`run-permission-rule-issue-codes-test.mjs` 直接读
|
|
15
|
+
* 实装 `@sema-agent/core` 的两份产物做双向对账(见下一段),core 一动这里就先红。
|
|
16
|
+
*
|
|
17
|
+
* -- 两个真源,而且它们**不相等** ------------------------------------------------------------
|
|
18
|
+
* (1) core 的 `permission-rules.js` 里**真被铸出来**的码 —— 这是 wire 上真会到达消费端的集合,
|
|
19
|
+
* {@link PERMISSION_RULE_ISSUE_CODES} 与它逐码等值;
|
|
20
|
+
* (2) core 的 `permission-rules.d.ts` 里 `code` 的**声明**联合 —— 它比 (1) 多一员
|
|
21
|
+
* `unsupported.unknown_param`。core 自己的 CHANGELOG(B15)写着那一员已**改名**成
|
|
22
|
+
* `unsupported.param_unknown`,联合里的旧名只是没删干净。收它进呈现表 = 复活一个引擎再也
|
|
23
|
+
* 不会铸的码(一个永不触发的分支),所以它单列在 {@link RETIRED_PERMISSION_RULE_ISSUE_CODES}
|
|
24
|
+
* 里记账,门按 `表 ∪ 退役表 == 声明联合` 对账。
|
|
25
|
+
*
|
|
26
|
+
* -- 两族码的**处置**不同,措辞必须说得出来 --------------------------------------------------
|
|
27
|
+
* · `invalid.*` = 这条规则**写错了**(语法/语义)—— 下一步是改规则;
|
|
28
|
+
* · `unsupported.*` = 规则本身合法,但**这条车道不接**(CC 里合法而 v1 不支持的 lane,或该去
|
|
29
|
+
* 另一个面落的常驻同意)—— 下一步是换一种写法、或把它落到规则店而不是策略折叠里。
|
|
30
|
+
* 两族绝不共用一句 —— 上游把它们分成两个前缀,正是因为「以为挡住了」比没挡更危险
|
|
31
|
+
* (`Bash(npm run:*)` 被当成 generic 参数规则接受后永不匹配,是这条面上最危险的失败形态)。
|
|
32
|
+
*
|
|
33
|
+
* -- UNTRUSTED --------------------------------------------------------------------------------
|
|
34
|
+
* `message` 是引擎/用户 settings 侧的内容(里面会带用户自己写的规则文本):只渲染,呈前**先转义、
|
|
35
|
+
* 后按转义结果封长**(与本包其余 detail 铸点同一条纪律),绝不当码用、绝不做等值匹配。
|
|
36
|
+
*/
|
|
37
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
38
|
+
/**
|
|
39
|
+
* 引擎**真会铸**的规则 lint 拒因码(core `PermissionRuleIssue.code`,7.9.x = 十五码)。
|
|
40
|
+
* 顺序与 core 声明同源。
|
|
41
|
+
* 🔴 这是一份**抄件**,不是本包的意见 —— 改它必须同 commit 附 core 坐标,且门会先红。
|
|
42
|
+
|
|
43
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
44
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
45
|
+
*/
|
|
46
|
+
export const PERMISSION_RULE_ISSUE_CODES = Object.freeze([
|
|
47
|
+
'invalid.empty_tool',
|
|
48
|
+
'invalid.paren',
|
|
49
|
+
'invalid.param_split',
|
|
50
|
+
'invalid.allow_param_rule',
|
|
51
|
+
'invalid.primary_field',
|
|
52
|
+
'invalid.cap_exceeded',
|
|
53
|
+
'unsupported.bash_prefix',
|
|
54
|
+
'invalid.command_rule',
|
|
55
|
+
'unsupported.param_unknown',
|
|
56
|
+
'unsupported.param_non_scalar',
|
|
57
|
+
'unsupported.tool_unknown',
|
|
58
|
+
'unsupported.file_glob',
|
|
59
|
+
'unsupported.path_base',
|
|
60
|
+
'unsupported.mcp_paren',
|
|
61
|
+
'unsupported.covering_paren',
|
|
62
|
+
]);
|
|
63
|
+
/**
|
|
64
|
+
* 在 core 的**声明**联合里还留着、但引擎**再也不铸**的码(记账用,不进呈现表)。
|
|
65
|
+
* · `unsupported.unknown_param` —— core 7.8.0(B15)改名成 `unsupported.param_unknown`,
|
|
66
|
+
* 联合里的旧名没删干净。
|
|
67
|
+
* 🔴 这张表**不是**给消费端查的,它是给门的:`表 ∪ 退役表 == 声明联合` 这条等式让「上游删掉那一员」
|
|
68
|
+
* 与「上游又留下一个新的 stale 成员」都当场红。查码一律查 {@link PERMISSION_RULE_ISSUE_CODES}。
|
|
69
|
+
* ⚠️ 另有一个更早的退役名 `unsupported.bash_prefix_rules_store`(sdk README 记的减员)——它在 core
|
|
70
|
+
* 7.9.x 的**声明里已经没有了**,所以不登记在这里(登记一个两个真源都不认的名字,门会红)。
|
|
71
|
+
|
|
72
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
73
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
74
|
+
*/
|
|
75
|
+
export const RETIRED_PERMISSION_RULE_ISSUE_CODES = Object.freeze(['unsupported.unknown_param']);
|
|
76
|
+
/** `message` 上屏封长(UTF-16 单元,按**转义后**长度算;它是一句话,给得比词宽)。 */
|
|
77
|
+
const ISSUE_MESSAGE_MAX = 200;
|
|
78
|
+
/**
|
|
79
|
+
* 逐码一句人话。**十五句刻意逐字互异**:每一条对写规则的人是一条不同的下一步。
|
|
80
|
+
*/
|
|
81
|
+
const ISSUE_SENTENCES = Object.freeze({
|
|
82
|
+
// -- invalid.*:规则写错了 ------------------------------------------------------------------
|
|
83
|
+
'invalid.empty_tool': 'this rule is invalid: it names no tool',
|
|
84
|
+
'invalid.paren': 'this rule is invalid: its parentheses do not parse (escape a literal one as \\( or \\))',
|
|
85
|
+
'invalid.param_split': 'this rule is invalid: the part before the colon is not a parameter name',
|
|
86
|
+
'invalid.allow_param_rule': 'this rule is invalid: an allow rule cannot carry a parameter pattern — only bare tool rules auto-allow',
|
|
87
|
+
'invalid.primary_field': "this rule is invalid: it patterns the tool's primary field, which matches the whole trimmed string rather than a prefix",
|
|
88
|
+
'invalid.cap_exceeded': 'this rule set is invalid: it is over one of the deployment caps (rule count, rule length, pattern length, or star count)',
|
|
89
|
+
'invalid.command_rule': 'this rule is invalid: the command pattern was rejected by the command-rule validator',
|
|
90
|
+
// -- unsupported.*:规则合法,但这条车道不接 ------------------------------------------------
|
|
91
|
+
'unsupported.bash_prefix': 'a standing approval for a command belongs in the persisted-rule store, so this allow rule is not supported here (deny and ask rules with the same shape do compile)',
|
|
92
|
+
'unsupported.param_unknown': 'this parameter rule is not supported: the tool carries no such parameter, so it could never match',
|
|
93
|
+
'unsupported.param_non_scalar': 'this parameter rule is not supported: that parameter holds an object or an array, so it could never match',
|
|
94
|
+
'unsupported.tool_unknown': "this rule is not supported on this leg: the resolver it supplied does not know the tool, so the rule's face cannot be checked",
|
|
95
|
+
'unsupported.file_glob': 'a standing approval by path belongs in the consent store, so this allow rule is not supported here (deny and ask path rules do compile)',
|
|
96
|
+
'unsupported.path_base': 'this path rule is not supported as spelled: it needs a base this policy was not given, so it would reach nothing and is refused rather than compiled',
|
|
97
|
+
'unsupported.mcp_paren': 'a parenthesised parameter rule is not supported on an MCP tool name',
|
|
98
|
+
'unsupported.covering_paren': 'a parenthesised parameter rule is not supported on a covering protocol name — the parameter grammar is keyed by an exact tool name, so no mounted tool could ever bear it',
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* 一个 lint 拒因 → 一句人话(+ 引擎自己那句更具体的散文)。**唯一措辞铸点**(三端共用;端零自拼)。
|
|
102
|
+
*
|
|
103
|
+
* 🔴 表外码的那一句说的是「**这个码比这一端新**」——码表的属主是引擎,加码不该被渲成故障;
|
|
104
|
+
* 原样带上那个码(运维要拿它去问上游),并**照样带上 `message`**:码读不懂不代表引擎那句话
|
|
105
|
+
* 读不懂,而那句话往往才是能动手改的那条信息。
|
|
106
|
+
* 🔴 `message` 是**散文不是码**:只渲染、呈前消毒 + 封长,绝不做等值匹配、绝不当分类位。
|
|
107
|
+
* 🔴 坏码 / 坏 message 一律不抛 —— 这条路径跑在「用户规则写错了」的那一刻,读器自己再抛一次
|
|
108
|
+
* 只会把一个可以说清的错误换成一个说不清的崩。
|
|
109
|
+
*/
|
|
110
|
+
export function permissionRuleIssueDetail(code, message) {
|
|
111
|
+
// 🔴 自有属性判据(异源对抗复审 [medium]):`Object.freeze` 不移除原型,`ISSUE_SENTENCES['toString']`
|
|
112
|
+
// 会命中 `Object.prototype` 上的函数并被当成「一句话」交出去。
|
|
113
|
+
const known = typeof code === 'string' && Object.hasOwn(ISSUE_SENTENCES, code) ? ISSUE_SENTENCES[code] : undefined;
|
|
114
|
+
const text = typeof message === 'string' && message.length > 0
|
|
115
|
+
? capForDisplay(message, ISSUE_MESSAGE_MAX)
|
|
116
|
+
: '';
|
|
117
|
+
const head = known !== undefined
|
|
118
|
+
? known
|
|
119
|
+
: `this rule was refused with ${typeof code === 'string' && code.length > 0 ? capForDisplay(code, 60) : '(no code)'}, a code newer than this client`;
|
|
120
|
+
return text.length > 0 ? `${head} — ${text}` : head;
|
|
121
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一个旋钮的值是谁定的 —— 四词闭集,**顺序即优先序**(engine 侧是穷举 switch,加词在那里编译红)。
|
|
3
|
+
* · `env` —— 这台机器的 env 变量钉的(部署主权,恒赢任何下发值);
|
|
4
|
+
* · `center` —— 配置中心发布的(重启生效;本机写同名 env 即把这个键收回);
|
|
5
|
+
* · `posture` —— 从**部署形**派生的(单用户 turnkey);
|
|
6
|
+
* · `engine-default` —— 没人钉过,内建缺省在岗。
|
|
7
|
+
* 🔴 本包按**开集**读实际值(词表属主在引擎侧,加词不该让消费端把一个合法读数判没);这张表是
|
|
8
|
+
* **优先序与判据**的真源,不是一道准入门。
|
|
9
|
+
|
|
10
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
11
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
12
|
+
*/
|
|
13
|
+
export declare const POSTURE_SOURCE_WORDS: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* 「**运维显式表态过吗**」——只有 `env` / `center` 两词算数。
|
|
16
|
+
*
|
|
17
|
+
* 🔴 `posture` 派生出来的 `true` **不是**「有人要求过」:它是部署形状的推论,换一台机器就变。
|
|
18
|
+
* 把它读成表态,会让「运维明确开过这个开关」这句话在一台谁都没碰过的机器上为真。
|
|
19
|
+
* 🔴 表外词(开集逃生口)一律**不算**表态:读不懂的来源不是「有人钉过」的证据。
|
|
20
|
+
*/
|
|
21
|
+
export declare function postureSourceIsOperatorPinned(source: string): boolean;
|
|
22
|
+
/** 一根旋钮的读数视图(sdk `PostureKnobReading<V>` 的防御读形;结构逐字同源,见文件尾对账钉)。 */
|
|
23
|
+
export interface PostureKnobView<V> {
|
|
24
|
+
/** 这根旋钮此刻的生效值。 */
|
|
25
|
+
value: V;
|
|
26
|
+
/**
|
|
27
|
+
* 这个值是谁定的。**按开集读**(闭四词见 {@link POSTURE_SOURCE_WORDS})。
|
|
28
|
+
* 🔴 `legacy` 为真时这一格**不是一次观测**,只是形上的占位 —— 机读一律走
|
|
29
|
+
* {@link postureKnobSourceOf}。
|
|
30
|
+
*/
|
|
31
|
+
source: string;
|
|
32
|
+
/** 给运维的指路句(逐键专属的散文,**不是机读码**:分类读 `source`,别对它做等值匹配)。
|
|
33
|
+
* UNTRUSTED-for-display:呈前消毒 + 封长,见 {@link postureKnobDetail}。 */
|
|
34
|
+
note: string;
|
|
35
|
+
/** 这份读数是从 <7.67.0 worker 的**裸值**形折出来的(那台 worker 说不出来源)。 */
|
|
36
|
+
legacy?: true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 这根旋钮的来源词,**答不出来就答不出来**。
|
|
40
|
+
* 🔴 legacy 行(老 worker 的裸值)⇒ `undefined`:形上的占位值不是一次观测,交出去就是编答案。
|
|
41
|
+
*/
|
|
42
|
+
export declare function postureKnobSourceOf(knob: PostureKnobView<unknown> | undefined): string | undefined;
|
|
43
|
+
/** `serverGates` 上三根 posture 旋钮的读数。 */
|
|
44
|
+
export interface ServerGateKnobsView {
|
|
45
|
+
/** 耐久审批门总开关。**本面的判别锚**:它读不出来 ⇒ 整段缺席(见 {@link projectServerGateKnobs})。 */
|
|
46
|
+
durableApproval: PostureKnobView<boolean>;
|
|
47
|
+
/** 审批窗(毫秒)。<7.67.0 整键缺席 ⇒ 这一格缺席 —— **绝不**铸 300000 的默认冒充读数。 */
|
|
48
|
+
streamAskWindowMs?: PostureKnobView<number>;
|
|
49
|
+
/** 会话自动起标题。同上,缺席即缺席。 */
|
|
50
|
+
sessionAutoTitle?: PostureKnobView<boolean>;
|
|
51
|
+
/** 这份读数整体是从老 worker 的裸值形折出来的。 */
|
|
52
|
+
legacy: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** 三根旋钮的名字(给人读的);措辞铸点按它取词,端零自拼。 */
|
|
55
|
+
export type PostureKnobName = 'durableApproval' | 'streamAskWindowMs' | 'sessionAutoTitle';
|
|
56
|
+
/**
|
|
57
|
+
* `wiring.serverGates` → 三旋钮读数;**畸形一律 `undefined`**,绝不抛出。
|
|
58
|
+
*
|
|
59
|
+
* 🔴 **判别锚是 `durableApproval`**:它是这一面上唯一**两代都在**的旋钮(老 worker 上是裸布尔,
|
|
60
|
+
* 新 worker 上是读数)。它读不出来 ⇒ 整段缺席 —— 一份没有主锚的旋钮面答不了运维要问的那一问,
|
|
61
|
+
* 而半段会被当成完整答案(与 `writeProtectionPosture` 缺 `source` ⇒ 整段缺席同一条纪律)。
|
|
62
|
+
* 🔴 **旁枝坏形只丢那一根**:`streamAskWindowMs` / `sessionAutoTitle` 各自独立,一根形坏不该把
|
|
63
|
+
* 另外两根一起藏起来(旁枝与主锚的处置刻意不同,理由见上一条)。
|
|
64
|
+
* 🔴 **缺席不铸默认**:另两根整键缺席时这两格就是缺席。铸 `300000` / `true` 会把「这台 worker
|
|
65
|
+
* 说不出来」渲成「它说了这个值」——那正是本面存在要根治的病。
|
|
66
|
+
* ⚠️ 「整键缺席(老 worker)」与「在场但形坏」在旁枝上折成同一个缺席;「为什么答不出来」不是
|
|
67
|
+
* 这一位该回答的(见 {@link postureKnobDetail} 的 `reachable` 段)。
|
|
68
|
+
*/
|
|
69
|
+
export declare function projectServerGateKnobs(wiring: unknown): ServerGateKnobsView | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* 四态措辞的**唯一铸点**(三端共用一句话;别在各端的行装配里另写一遍 —— 与
|
|
72
|
+
* `readFacePostureDetail` / `writeProtectionDoctorDetail` 同一条纪律)。
|
|
73
|
+
*
|
|
74
|
+
* 🔴 四句刻意**逐字互异**(黑盒锚),对运维是四条不同的下一步:
|
|
75
|
+
* ① 读数在场 —— `<旋钮> <值> (source: <来源> — <指路句>)`;
|
|
76
|
+
* ② 读数在场但来自老 worker 的裸值(`legacy`)—— 报得出**值**,但逐字点明「这台 worker 说不出
|
|
77
|
+
* 是谁定的」;**句中不出现任何来源词**(冒称来源比不说更坏:它会让人以为查过了);
|
|
78
|
+
* ③ `opts.reachable === false` —— 「未观测」:这一次进程没读到 operator 响应;
|
|
79
|
+
* ④ `reachable:true` 但读数仍缺席 —— 「不报」:响应读到了,只是这一位读不出来。
|
|
80
|
+
* **不武断咎为版本**:整键缺席(老引擎)与在场却形坏折成同一个缺席,句子不能替一种情形撒谎。
|
|
81
|
+
* 🔴 ③④ 两句**一个值都不报** —— 它们要是顺口说了 `off`,消费端就会把「读不出」当成「关着」。
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ **数值一律按毫秒读**:本面今天唯一的数值旋钮就是那只毫秒窗(门里有一条对 sdk spec 的钉:
|
|
84
|
+
* `ServerWiringGates` 上 `type: number` 的旋钮恰一根)。上游哪天加第二根非时长数值旋钮,那条
|
|
85
|
+
* 钉先红,免得这里的措辞开始对它撒谎。
|
|
86
|
+
*/
|
|
87
|
+
export declare function postureKnobDetail(knob: PostureKnobView<boolean | number> | undefined, opts: {
|
|
88
|
+
reachable: boolean;
|
|
89
|
+
knob: PostureKnobName;
|
|
90
|
+
}): string;
|