@sema-agent/client-core 0.62.2 → 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 +95 -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 +4 -2
- package/dist/toolRoster.d.ts +182 -0
- package/dist/toolRoster.js +236 -0
- package/docs/INTEGRATION-CLIENTS.md +451 -9
- package/package.json +4 -4
|
@@ -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;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
|
+
/**
|
|
3
|
+
* 一个旋钮的值是谁定的 —— 四词闭集,**顺序即优先序**(engine 侧是穷举 switch,加词在那里编译红)。
|
|
4
|
+
* · `env` —— 这台机器的 env 变量钉的(部署主权,恒赢任何下发值);
|
|
5
|
+
* · `center` —— 配置中心发布的(重启生效;本机写同名 env 即把这个键收回);
|
|
6
|
+
* · `posture` —— 从**部署形**派生的(单用户 turnkey);
|
|
7
|
+
* · `engine-default` —— 没人钉过,内建缺省在岗。
|
|
8
|
+
* 🔴 本包按**开集**读实际值(词表属主在引擎侧,加词不该让消费端把一个合法读数判没);这张表是
|
|
9
|
+
* **优先序与判据**的真源,不是一道准入门。
|
|
10
|
+
|
|
11
|
+
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
12
|
+
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
13
|
+
*/
|
|
14
|
+
export const POSTURE_SOURCE_WORDS = Object.freeze(['env', 'center', 'posture', 'engine-default']);
|
|
15
|
+
/**
|
|
16
|
+
* 「**运维显式表态过吗**」——只有 `env` / `center` 两词算数。
|
|
17
|
+
*
|
|
18
|
+
* 🔴 `posture` 派生出来的 `true` **不是**「有人要求过」:它是部署形状的推论,换一台机器就变。
|
|
19
|
+
* 把它读成表态,会让「运维明确开过这个开关」这句话在一台谁都没碰过的机器上为真。
|
|
20
|
+
* 🔴 表外词(开集逃生口)一律**不算**表态:读不懂的来源不是「有人钉过」的证据。
|
|
21
|
+
*/
|
|
22
|
+
export function postureSourceIsOperatorPinned(source) {
|
|
23
|
+
return source === 'env' || source === 'center';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 这根旋钮的来源词,**答不出来就答不出来**。
|
|
27
|
+
* 🔴 legacy 行(老 worker 的裸值)⇒ `undefined`:形上的占位值不是一次观测,交出去就是编答案。
|
|
28
|
+
*/
|
|
29
|
+
export function postureKnobSourceOf(knob) {
|
|
30
|
+
if (knob === undefined || knob.legacy === true)
|
|
31
|
+
return undefined;
|
|
32
|
+
return knob.source;
|
|
33
|
+
}
|
|
34
|
+
/** 旋钮 → 人话名。**唯一铸点**(三端共用一套词;别在各端的行装配里另起一份)。 */
|
|
35
|
+
const KNOB_LABELS = Object.freeze({
|
|
36
|
+
durableApproval: 'durable approval',
|
|
37
|
+
streamAskWindowMs: 'approval window',
|
|
38
|
+
sessionAutoTitle: 'session auto-title',
|
|
39
|
+
});
|
|
40
|
+
/** `note` 上屏封长(UTF-16 单元,按**转义后**的长度算;与 readFace 那一格同值同理由)。 */
|
|
41
|
+
const KNOB_NOTE_MAX = 256;
|
|
42
|
+
/** `source` 上屏封长(同上)。 */
|
|
43
|
+
const KNOB_WORD_MAX = 40;
|
|
44
|
+
/** 一只 `{value, source, note}` 读数的公共窄读(值判据由调用方给)。坏形 ⇒ `undefined`。 */
|
|
45
|
+
function readKnob(raw, valueOk) {
|
|
46
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
47
|
+
return undefined;
|
|
48
|
+
const r = raw;
|
|
49
|
+
if (!valueOk(r.value))
|
|
50
|
+
return undefined;
|
|
51
|
+
if (typeof r.source !== 'string' || r.source.length === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
// `note` 允许空串(自由文本;空文本本身也是一句读数,不是「读不出」)。
|
|
54
|
+
if (typeof r.note !== 'string')
|
|
55
|
+
return undefined;
|
|
56
|
+
return { value: r.value, source: r.source, note: r.note };
|
|
57
|
+
}
|
|
58
|
+
const isBool = (v) => typeof v === 'boolean';
|
|
59
|
+
/** 窗值:有限**非负**数(负窗在引擎侧是拒启门,一个负数读数是坏形不是事实)。 */
|
|
60
|
+
const isWindowMs = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
61
|
+
/**
|
|
62
|
+
* `wiring.serverGates` → 三旋钮读数;**畸形一律 `undefined`**,绝不抛出。
|
|
63
|
+
*
|
|
64
|
+
* 🔴 **判别锚是 `durableApproval`**:它是这一面上唯一**两代都在**的旋钮(老 worker 上是裸布尔,
|
|
65
|
+
* 新 worker 上是读数)。它读不出来 ⇒ 整段缺席 —— 一份没有主锚的旋钮面答不了运维要问的那一问,
|
|
66
|
+
* 而半段会被当成完整答案(与 `writeProtectionPosture` 缺 `source` ⇒ 整段缺席同一条纪律)。
|
|
67
|
+
* 🔴 **旁枝坏形只丢那一根**:`streamAskWindowMs` / `sessionAutoTitle` 各自独立,一根形坏不该把
|
|
68
|
+
* 另外两根一起藏起来(旁枝与主锚的处置刻意不同,理由见上一条)。
|
|
69
|
+
* 🔴 **缺席不铸默认**:另两根整键缺席时这两格就是缺席。铸 `300000` / `true` 会把「这台 worker
|
|
70
|
+
* 说不出来」渲成「它说了这个值」——那正是本面存在要根治的病。
|
|
71
|
+
* ⚠️ 「整键缺席(老 worker)」与「在场但形坏」在旁枝上折成同一个缺席;「为什么答不出来」不是
|
|
72
|
+
* 这一位该回答的(见 {@link postureKnobDetail} 的 `reachable` 段)。
|
|
73
|
+
*/
|
|
74
|
+
export function projectServerGateKnobs(wiring) {
|
|
75
|
+
if (typeof wiring !== 'object' || wiring === null || Array.isArray(wiring))
|
|
76
|
+
return undefined;
|
|
77
|
+
const sg = wiring.serverGates;
|
|
78
|
+
if (typeof sg !== 'object' || sg === null || Array.isArray(sg))
|
|
79
|
+
return undefined;
|
|
80
|
+
const g = sg;
|
|
81
|
+
const raw = g.durableApproval;
|
|
82
|
+
let durableApproval;
|
|
83
|
+
let legacy = false;
|
|
84
|
+
if (typeof raw === 'boolean') {
|
|
85
|
+
// 老 worker(<7.67.0)的裸布尔:折进同一只壳,但立 legacy 位。`source` 是形上的占位,
|
|
86
|
+
// **不是**一次观测 —— 机读走 `postureKnobSourceOf`(它对 legacy 行答 undefined)。
|
|
87
|
+
durableApproval = { value: raw, source: 'engine-default', note: '', legacy: true };
|
|
88
|
+
legacy = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
durableApproval = readKnob(raw, isBool);
|
|
92
|
+
}
|
|
93
|
+
if (durableApproval === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
const streamAskWindowMs = readKnob(g.streamAskWindowMs, isWindowMs);
|
|
96
|
+
const sessionAutoTitle = readKnob(g.sessionAutoTitle, isBool);
|
|
97
|
+
return {
|
|
98
|
+
durableApproval,
|
|
99
|
+
...(streamAskWindowMs !== undefined ? { streamAskWindowMs } : {}),
|
|
100
|
+
...(sessionAutoTitle !== undefined ? { sessionAutoTitle } : {}),
|
|
101
|
+
legacy,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 毫秒 → 人话。
|
|
106
|
+
* 🔴 **只在整除时才升单位**:`90m` 比 `1.5h` 更不容易被读错,而 `5400000ms` 谁也读不出是一个半
|
|
107
|
+
* 小时。除不尽 ⇒ 原样报毫秒(诚实优于好看)。
|
|
108
|
+
*/
|
|
109
|
+
function humanMs(ms) {
|
|
110
|
+
// 天不单列:`24h` 比 `1d` 更贴近运维钉回时写的那个数(`STREAM_ASK_WINDOW_MS`),小时这一档
|
|
111
|
+
// 已经把它读成人话了。
|
|
112
|
+
if (ms > 0 && ms % 3600000 === 0)
|
|
113
|
+
return `${ms / 3600000}h`;
|
|
114
|
+
if (ms > 0 && ms % 60000 === 0)
|
|
115
|
+
return `${ms / 60000}m`;
|
|
116
|
+
if (ms > 0 && ms % 1000 === 0)
|
|
117
|
+
return `${ms / 1000}s`;
|
|
118
|
+
return `${ms}ms`;
|
|
119
|
+
}
|
|
120
|
+
/** 值 → 上屏词。布尔渲 `on`/`off`(`true`/`false` 是给机器读的);数值按毫秒渲人话。 */
|
|
121
|
+
function knobValueWord(value) {
|
|
122
|
+
if (typeof value === 'boolean')
|
|
123
|
+
return value ? 'on' : 'off';
|
|
124
|
+
return humanMs(value);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 四态措辞的**唯一铸点**(三端共用一句话;别在各端的行装配里另写一遍 —— 与
|
|
128
|
+
* `readFacePostureDetail` / `writeProtectionDoctorDetail` 同一条纪律)。
|
|
129
|
+
*
|
|
130
|
+
* 🔴 四句刻意**逐字互异**(黑盒锚),对运维是四条不同的下一步:
|
|
131
|
+
* ① 读数在场 —— `<旋钮> <值> (source: <来源> — <指路句>)`;
|
|
132
|
+
* ② 读数在场但来自老 worker 的裸值(`legacy`)—— 报得出**值**,但逐字点明「这台 worker 说不出
|
|
133
|
+
* 是谁定的」;**句中不出现任何来源词**(冒称来源比不说更坏:它会让人以为查过了);
|
|
134
|
+
* ③ `opts.reachable === false` —— 「未观测」:这一次进程没读到 operator 响应;
|
|
135
|
+
* ④ `reachable:true` 但读数仍缺席 —— 「不报」:响应读到了,只是这一位读不出来。
|
|
136
|
+
* **不武断咎为版本**:整键缺席(老引擎)与在场却形坏折成同一个缺席,句子不能替一种情形撒谎。
|
|
137
|
+
* 🔴 ③④ 两句**一个值都不报** —— 它们要是顺口说了 `off`,消费端就会把「读不出」当成「关着」。
|
|
138
|
+
*
|
|
139
|
+
* ⚠️ **数值一律按毫秒读**:本面今天唯一的数值旋钮就是那只毫秒窗(门里有一条对 sdk spec 的钉:
|
|
140
|
+
* `ServerWiringGates` 上 `type: number` 的旋钮恰一根)。上游哪天加第二根非时长数值旋钮,那条
|
|
141
|
+
* 钉先红,免得这里的措辞开始对它撒谎。
|
|
142
|
+
*/
|
|
143
|
+
export function postureKnobDetail(knob, opts) {
|
|
144
|
+
// 🔴 **按自有属性查表**(同形族扫):键在型面上是闭三词,但本口吃的是运行期值 —— 一个 JS 调用方
|
|
145
|
+
// 塞进 `toString`,裸下标会命中 `Object.prototype` 上的函数并被拼进句子。读不出名字时退到
|
|
146
|
+
// 一个中性词:**名字读不出不该让「这根旋钮的值是什么」也一起说不出来**。
|
|
147
|
+
const label = Object.hasOwn(KNOB_LABELS, opts.knob) ? KNOB_LABELS[opts.knob] : 'posture knob';
|
|
148
|
+
if (knob !== undefined) {
|
|
149
|
+
const word = knobValueWord(knob.value);
|
|
150
|
+
if (knob.legacy === true) {
|
|
151
|
+
return `${label} ${word} (this worker does not report who set it — an engine below 7.67.0)`;
|
|
152
|
+
}
|
|
153
|
+
const source = capForDisplay(knob.source, KNOB_WORD_MAX);
|
|
154
|
+
const note = capForDisplay(knob.note, KNOB_NOTE_MAX);
|
|
155
|
+
return note.length > 0
|
|
156
|
+
? `${label} ${word} (source: ${source} — ${note})`
|
|
157
|
+
: `${label} ${word} (source: ${source})`;
|
|
158
|
+
}
|
|
159
|
+
if (!opts.reachable) {
|
|
160
|
+
return `${label} not observed (this end could not read the engine's diagnostics)`;
|
|
161
|
+
}
|
|
162
|
+
return `${label} not reported by this engine (an engine below 7.67.0, or a response this end could not parse)`;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* **编译期对账钉**(不出公面):sdk 的读数形必须能赋给本视图,`ServerWiringGates` 的三根旋钮
|
|
166
|
+
* 必须真是读数形 —— 上游改形时这一行是本包第一个红的地方。
|
|
167
|
+
*
|
|
168
|
+
* 🔴 反向**刻意不钉**(本视图不必能赋给 sdk 形):`source` 在本视图上放宽成任意 `string`(开集读),
|
|
169
|
+
* 那是**故意比铸点宽** —— 窄读域只许等于或宽于铸点域,钉反向会把这条纪律反过来判成错。
|
|
170
|
+
*/
|
|
171
|
+
const _postureKnobShapePin = (w) => w;
|
|
172
|
+
void _postureKnobShapePin;
|
|
173
|
+
const _serverGateKnobsShapePin = (g) => g;
|
|
174
|
+
void _serverGateKnobsShapePin;
|
package/dist/steering.d.ts
CHANGED
|
@@ -50,7 +50,12 @@ export interface EngineTaskStatusAttachment {
|
|
|
50
50
|
deltaSummary: null;
|
|
51
51
|
}
|
|
52
52
|
export type SteeringRenderable = SteeringInjectedAttachment | EngineTaskStatusAttachment;
|
|
53
|
-
/** Default-arm label for a source this shell build predates.
|
|
53
|
+
/** Default-arm label for a source this shell build predates.
|
|
54
|
+
*
|
|
55
|
+
* 🔴 **按自有属性查表**(0.63.0 同形存量清剿;与 `gateVocabulary` / `permissionRuleIssue` /
|
|
56
|
+
* `engineNoticeCodes` 三处同一条病形):`SOURCE_LABELS` 是对象表而 `source` **来自 wire**,
|
|
57
|
+
* 裸下标会命中 `Object.prototype` 上的成员 —— `steeringLabel('constructor')` 返回一个**函数**,
|
|
58
|
+
* 而 `??` 对函数不生效 ⇒ 它被原样拼进 attachment 的 label。默认臂在这一形上等于不存在。 */
|
|
54
59
|
export declare function steeringLabel(source: string): string;
|
|
55
60
|
/**
|
|
56
61
|
* Project one `steering_injected` wire event into its renderable attachment(s). Always returns at
|
package/dist/steering.js
CHANGED
|
@@ -38,9 +38,17 @@ const SOURCE_LABELS = {
|
|
|
38
38
|
background_tasks: 'Background tasks from before compaction announced to the model',
|
|
39
39
|
tools_delta: 'Newly available tools announced to the model',
|
|
40
40
|
};
|
|
41
|
-
/** Default-arm label for a source this shell build predates.
|
|
41
|
+
/** Default-arm label for a source this shell build predates.
|
|
42
|
+
*
|
|
43
|
+
* 🔴 **按自有属性查表**(0.63.0 同形存量清剿;与 `gateVocabulary` / `permissionRuleIssue` /
|
|
44
|
+
* `engineNoticeCodes` 三处同一条病形):`SOURCE_LABELS` 是对象表而 `source` **来自 wire**,
|
|
45
|
+
* 裸下标会命中 `Object.prototype` 上的成员 —— `steeringLabel('constructor')` 返回一个**函数**,
|
|
46
|
+
* 而 `??` 对函数不生效 ⇒ 它被原样拼进 attachment 的 label。默认臂在这一形上等于不存在。 */
|
|
42
47
|
export function steeringLabel(source) {
|
|
43
|
-
|
|
48
|
+
const known = typeof source === 'string' && Object.hasOwn(SOURCE_LABELS, source)
|
|
49
|
+
? SOURCE_LABELS[source]
|
|
50
|
+
: undefined;
|
|
51
|
+
return known ?? `Steering injected (${source})`;
|
|
44
52
|
}
|
|
45
53
|
/**
|
|
46
54
|
* core turn-attachments.ts renderBackgroundTasks line shape (198 vbl VERBATIM phrases):
|
|
@@ -97,7 +97,11 @@ export interface SubagentContentEvent {
|
|
|
97
97
|
* 「这一位是增量还是全文」变成读者要靠 `type` 去反推的事,而反推错的代价是内容重复上屏。 */
|
|
98
98
|
text?: string | undefined;
|
|
99
99
|
/** wire 上那一帧的**事件身份**(聚合两臂的幂等键;缺席 ⇒ 退回内容判据,见 publish 的两道闸)。
|
|
100
|
-
* 🔴
|
|
100
|
+
* 🔴 工具两臂**不拿它当身份**:那次调用的身份是 `toolCallId`,账本是 `items` 上那张卡。
|
|
101
|
+
* ⚠️ 但两臂**确实读它**(0.62.2 起):它是那张卡逐半的**阶段水位**({@link CardPhase} 的
|
|
102
|
+
* `start` / `end`),回答「这一发比卡上现在承载的那一阶段新还是旧」。缺席是一个值 ——
|
|
103
|
+
* 身份缺席而内容真的变了 ⇒ 那一半的水位被清掉(判不出新旧就诚实地说判不出)。
|
|
104
|
+
* 旧注写的「工具两臂不读这一位」自 0.62.2 起不成立。 */
|
|
101
105
|
eventId?: string | undefined;
|
|
102
106
|
toolCallId?: string | undefined;
|
|
103
107
|
toolName?: string | undefined;
|