@sema-agent/client-core 0.76.0 → 0.76.2
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 +30 -0
- package/README.md +7 -1
- package/dist/adapt/arms.js +51 -48
- package/dist/adapt/ids.d.ts +29 -2
- package/dist/adapt/ids.js +29 -2
- package/dist/adapt/panelTasks.d.ts +4 -3
- package/dist/adapt/panelTasks.js +29 -11
- package/dist/adapt/textStream.js +5 -5
- package/dist/adapt/toolCards.js +2 -2
- package/dist/adapt/turnFlags.js +5 -5
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +8 -8
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +12 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +13 -20
- package/dist/adapter/runStream.js +14 -5
- package/dist/adapter/types.d.ts +12 -0
- package/dist/adapter/types.js +30 -0
- package/dist/approvalsStreamLiveCapability.js +15 -35
- package/dist/deviceExecutorManagementCapability.js +15 -35
- package/dist/engineCapReader.d.ts +77 -0
- package/dist/engineCapReader.js +87 -0
- package/dist/executionLaneCapability.js +15 -38
- package/dist/hitl/persistedRulesWire.d.ts +20 -0
- package/dist/hitl/persistedRulesWire.js +23 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -1
- package/dist/mcpLiveness.d.ts +179 -0
- package/dist/mcpLiveness.js +218 -0
- package/dist/mcpPanel.d.ts +17 -0
- package/dist/mcpPanel.js +21 -0
- package/dist/memoryComplianceCapability.d.ts +73 -0
- package/dist/memoryComplianceCapability.js +109 -0
- package/dist/memoryEntriesWire.d.ts +302 -0
- package/dist/memoryEntriesWire.js +595 -0
- package/dist/memoryOriginCapability.d.ts +68 -0
- package/dist/memoryOriginCapability.js +102 -0
- package/dist/peerLaneCapability.d.ts +62 -0
- package/dist/peerLaneCapability.js +100 -0
- package/dist/permissionRulesWriteCapability.d.ts +64 -0
- package/dist/permissionRulesWriteCapability.js +98 -0
- package/dist/selfOrchestrationDenial.js +7 -2
- package/dist/sqlEngineCapability.js +15 -35
- package/dist/webSearchBackendCapability.js +15 -35
- package/dist/writeProtectionCapability.js +15 -35
- package/docs/INTEGRATION-CLIENTS.md +107 -11
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memoryOriginCapability — `GET /v1/capabilities.memoryOrigin` 的三端共用读面(0.76.1 CC-97a;
|
|
3
|
+
* 与 `memoryCompliance` 及其余能力位兄弟同一套四态词汇,四口走共用工厂 {@link createEngineCapReader})。
|
|
4
|
+
*
|
|
5
|
+
* ── 这一位答的是哪一个问题 ────────────────────────────────────────────────────────────────
|
|
6
|
+
* 「**外源标记人面三口**(`GET /v1/memory/origin/external` · `GET /v1/memory/origin/clearances` ·
|
|
7
|
+
* `POST /v1/memory/origin/entries/:entryId/clear`,operator 车道)在这台引擎上在不在」。引擎侧的谓词
|
|
8
|
+
* 是**两项合取**:外源面在挂载期判出后端自带引擎控制面归属(缺则整口不挂),且 operator 名单**非空**
|
|
9
|
+
* (空名单下三口对任何身份恒 403)。
|
|
10
|
+
* ⚠️ 位为真**只**保证「门能过 + 面在场」,**不**保证这一次调用成功:清标账本损坏是**运行期**事实
|
|
11
|
+
* (typed 500)。
|
|
12
|
+
* ⚠️ 与 `memoryCompliance` **同判据不同位**:两族今天查后端的同一个面,但它们是**两个产品面**
|
|
13
|
+
* (合规问询 / 抹除 · 外源审计 / 清标),一个部署可以只想开其中一族 —— 引擎**刻意不合并**,
|
|
14
|
+
* 即使今天两位恒同值。🔴 本包跟着不合并:两只读器各持一张 per-baseUrl 表,喂其中一只畸形回体
|
|
15
|
+
* 只删它自己那一格,{@link import('./memoryComplianceCapability.js')} 的读数一字不变。把两位
|
|
16
|
+
* 折成一只读器 = 在未来的分家上让消费端读到假话。
|
|
17
|
+
* 🔴 位为真**也不**保证 `GET …/external` 的答案**完备**:引擎 v1 没有 scope 枚举读面,答案只覆盖
|
|
18
|
+
* 调用方点名的那些 scope ⇒ **空答绝不读作「本店干净」**。那是**发现链**的事,能力位表达不了它 ——
|
|
19
|
+
* 本包把这句话实现在导出条目面的判词口上(`scopeExternalOriginVerdict`),不是只写在注释里。
|
|
20
|
+
*
|
|
21
|
+
* ── 🔴 读法(与 `memoryCompliance` 逐条同律,但**各自一张表**)─────────────────────────────
|
|
22
|
+
* ① 引擎铸这一位时**恒在场**(一格布尔)⇒ **键缺席 = 老引擎不报这一位** ⇒ `not_reported`(判不出),
|
|
23
|
+
* 绝不折成 `false`;
|
|
24
|
+
* ② `false` 是**明确的否** ⇒ `absent`;成因(后端无控制面归属 / operator 名单为空)在 wire 上不可分,
|
|
25
|
+
* 措辞不推因由;
|
|
26
|
+
* ③ 严格布尔;`null` / 串 / `0` / `1` / 数组 / 对象一律畸形 ⇒ 删格 ⇒ `unobserved`,🔴 绝不折 `absent`;
|
|
27
|
+
* ③b caps 回体本身不是对象(`null` / 数组 / 串 / 数)⇒ 畸形删格,**不是** `not_reported`(那一态只说「这是
|
|
28
|
+
* 一份 caps,但没有这一格」)。
|
|
29
|
+
* ④ 判在场用 `hasOwn`(原型键永不上 wire);
|
|
30
|
+
* ⑤ 「三口能不能用」的判据归包:{@link memoryOriginVerbsAvailable}。
|
|
31
|
+
*/
|
|
32
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
33
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
34
|
+
import { MEMORY_ENGINE_REQUIRED_CODE } from './memoryComplianceCapability.js';
|
|
35
|
+
/** caps 回体 → 本格读数;畸形一律 `undefined`(= 这一格不写 ⇒ 读口答 `unobserved`)。 */
|
|
36
|
+
export function projectMemoryOriginCapability(caps) {
|
|
37
|
+
// 🔴 数组也是畸形:一个数组回体**不是**引擎对 caps 的回答,从它上面读出「老引擎不报这一位」是替引擎
|
|
38
|
+
// 编了一句它没说的话。`not_reported` 只留给**真的是 caps 对象、但这一格不在**那一形。
|
|
39
|
+
if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
|
|
40
|
+
return undefined;
|
|
41
|
+
const c = caps;
|
|
42
|
+
if (!Object.hasOwn(c, 'memoryOrigin'))
|
|
43
|
+
return { kind: 'not_reported' };
|
|
44
|
+
// 🔴 只读一次(与兄弟同律):变化的 getter 不许让校验与分臂看到两个不同的值。
|
|
45
|
+
const v = c.memoryOrigin;
|
|
46
|
+
if (v === undefined)
|
|
47
|
+
return { kind: 'not_reported' };
|
|
48
|
+
if (typeof v !== 'boolean')
|
|
49
|
+
return undefined;
|
|
50
|
+
return v ? { kind: 'present' } : { kind: 'absent' };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 本格的 per-baseUrl 读账 + 四口 —— 走共用工厂;本文件只留投影函数、doctor 措辞表与本位判据助手。
|
|
54
|
+
* 🔴 这一张 Map 与 `memoryCompliance` 那一张**是两张**:同判据不同位,读账也不共用。
|
|
55
|
+
*/
|
|
56
|
+
const reader = createEngineCapReader({
|
|
57
|
+
name: 'memoryOrigin',
|
|
58
|
+
project: projectMemoryOriginCapability,
|
|
59
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
60
|
+
});
|
|
61
|
+
/** 宿主 caps probe 的读面 tee 落点。绝不 throw;畸形 ⇒ 删格;`opts.generation` 关掉旧探测覆盖新读数的竞态。 */
|
|
62
|
+
export function noteEngineCapsForMemoryOrigin(baseUrl, caps, opts) {
|
|
63
|
+
reader.note(baseUrl, caps, opts);
|
|
64
|
+
}
|
|
65
|
+
/** 本进程观测到的读数;这一格空缺 ⇒ `{kind:'unobserved'}`。 */
|
|
66
|
+
export function observedMemoryOrigin(baseUrl = engineWireTarget()?.baseUrl) {
|
|
67
|
+
return reader.observed(baseUrl);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 「外源标记人面三口能不能用」的判据单源(三态):只有引擎明说在场才 `'yes'`;明说不在 ⇒ `'no'`;
|
|
71
|
+
* 老引擎不报 / 从没观测 ⇒ `'unknown'`。
|
|
72
|
+
* 🔴 `'yes'` **只**说三口够得着,对外源清单的**完备性**一个字都没说(那一问归
|
|
73
|
+
* `scopeExternalOriginVerdict`)。
|
|
74
|
+
*/
|
|
75
|
+
export function memoryOriginVerbsAvailable(reading) {
|
|
76
|
+
if (reading.kind === 'present')
|
|
77
|
+
return 'yes';
|
|
78
|
+
if (reading.kind === 'absent')
|
|
79
|
+
return 'no';
|
|
80
|
+
return 'unknown';
|
|
81
|
+
}
|
|
82
|
+
/** doctor 那一行的 detail 串,唯一措辞真源。🔴 两种「读不出」的句子都不许暗示「这台没有外源面」。 */
|
|
83
|
+
export function memoryOriginDoctorDetail(reading) {
|
|
84
|
+
switch (reading.kind) {
|
|
85
|
+
case 'unobserved':
|
|
86
|
+
return 'external-origin face not observed — the engine reports it on /v1/capabilities (memoryOrigin); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
|
|
87
|
+
case 'not_reported':
|
|
88
|
+
return 'external-origin face not reported by this engine — only newer engines advertise it; this does not say whether the origin listing and clearance endpoints exist, probe them directly';
|
|
89
|
+
case 'absent':
|
|
90
|
+
return `external-origin face off on this deployment — the origin listing and clearance endpoints are unavailable (${MEMORY_ENGINE_REQUIRED_CODE}); the engine does not say which half is missing (a memory backend that does not own the engine control plane, or an empty operator roster), so do not guess`;
|
|
91
|
+
case 'present':
|
|
92
|
+
return 'external-origin face on — the origin listing and clearance endpoints are mounted and the operator roster is non-empty on this engine; this says the door opens, not that any one call succeeds, and it says nothing about the listing being complete (the engine has no scope-enumeration read face, so an answer covers only the scopes the caller named and an empty one must never be read as a clean store)';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** 换代失效口(引擎温切成功后调):清成未观测。空串 ⇒ no-op;绝不 throw。 */
|
|
96
|
+
export function forgetMemoryOriginReading(baseUrl) {
|
|
97
|
+
reader.forget(baseUrl);
|
|
98
|
+
}
|
|
99
|
+
/** 测试钩子。 */
|
|
100
|
+
export function __resetMemoryOriginReadingsForTests() {
|
|
101
|
+
reader.__resetForTests();
|
|
102
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peerLaneCapability — `GET /v1/capabilities.peerLane`(server ≥7.91.1;sdk 9.8.1 尚未声明该键 ⇒ 结构读)
|
|
3
|
+
* 的三端共用读面(0.76.1 CC-102;与 sql / writeProtection / webSearch.backend / executionLane /
|
|
4
|
+
* approvalsStreamLive / deviceExecutor.management 各只兄弟同一套四态词汇)。
|
|
5
|
+
*
|
|
6
|
+
* ── 这一位答的是哪一个问题 ────────────────────────────────────────────────────────────────
|
|
7
|
+
* 「本部署的会话之间能不能互相发现并投递」——引擎的跨会话目录席是否已挂(会话枚举面 ∧ 耐久
|
|
8
|
+
* mailbox ∧ 该 mailbox 的收件人生命周期面,三缺一即缺席)。位为真时模型侧多出 `ListAgents`
|
|
9
|
+
* (列同一 principal 的其它会话)、`SendMessage` 的 `session.<id>` 地址臂,以及每 turn 边界对本
|
|
10
|
+
* 会话那只会话盒的排水。
|
|
11
|
+
*
|
|
12
|
+
* ── 🔴 本位不是 HTTP 面 ────────────────────────────────────────────────────────────────────
|
|
13
|
+
* 上游没有为这一族能力单开任何新端点 —— 会话清单仍走既有的会话列举口。所以本位的消费方式是
|
|
14
|
+
* **渲染 / 预期**(要不要露出跨会话的入口),不是拿它去 gate 某一个动词的调用。
|
|
15
|
+
* ⚠️ 辖域只到「席」:上游另有一道装配期挂载判据(mailbox 必须显式声明跨进程安全),不齐时会在
|
|
16
|
+
* 装配期响亮拒挂并通告运维;本读面刻意不复判那一道 —— 复判 = 给同一件事立第二个判官。
|
|
17
|
+
*
|
|
18
|
+
* ── 🔴 四态,不是两态([honest-absence-not-fabricated-zero])──────────────────────────────────
|
|
19
|
+
* ① `unobserved` —— 本进程一次 caps 响应都没观测到。
|
|
20
|
+
* ② `not_reported` —— caps 观测到了,而回体上**没有** `peerLane` 这个键 ⇒ 老 worker(比 7.91.1 老)。
|
|
21
|
+
* 这台部署有没有跨会话车道**判不出**,不是「没有」。
|
|
22
|
+
* ③ `absent` —— 键在、值**明确是** `false`:这台部署没有挂跨会话目录席。**正面事实**,
|
|
23
|
+
* 与 `not_reported`(判不出)是两件不同的事,处置也相反。
|
|
24
|
+
* ④ `present` —— 键在、值为 `true`:席已挂。
|
|
25
|
+
* 值既不是 `true` 也不是 `false`(串 / 数 / null / 数组 / 对象)⇒ 畸形 ⇒ 删格(⇒ `unobserved`)。
|
|
26
|
+
* 🔴 三种「没有」(`unobserved` / `not_reported` / `absent`)**不许互折** —— 折起来会把「判不出」
|
|
27
|
+
* 说成「引擎明说没有」,或把「一次都没问过」说成「问过、答案是没有」。
|
|
28
|
+
*/
|
|
29
|
+
/** 四态读数。`unobserved` 由读口在这一格空缺时铸,不由投影铸。 */
|
|
30
|
+
export type PeerLaneReading = {
|
|
31
|
+
kind: 'unobserved';
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'not_reported';
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'absent';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'present';
|
|
38
|
+
};
|
|
39
|
+
/** caps 回体 → 本格读数;畸形一律 `undefined`(= 这一格不写 ⇒ 读口答 `unobserved`)。 */
|
|
40
|
+
export declare function projectPeerLaneCapability(caps: unknown): PeerLaneReading | undefined;
|
|
41
|
+
/** 宿主 caps probe 的读面 tee 落点。绝不 throw;畸形 ⇒ 删格;`opts.generation` 关掉旧探测覆盖新读数的竞态。 */
|
|
42
|
+
export declare function noteEngineCapsForPeerLane(baseUrl: string, caps: unknown, opts?: {
|
|
43
|
+
generation?: number;
|
|
44
|
+
}): void;
|
|
45
|
+
/** 本进程观测到的读数;这一格空缺 ⇒ `{kind:'unobserved'}`。 */
|
|
46
|
+
export declare function observedPeerLane(baseUrl?: string | undefined): PeerLaneReading;
|
|
47
|
+
/**
|
|
48
|
+
* 「要不要在壳上露出跨会话入口(`ListAgents` / `SendMessage` 的 `session.<id>` 地址臂)」的判据
|
|
49
|
+
* 单源(三态):只有引擎明说挂了(`present`)才 `'yes'`;明说没挂(`absent`)⇒ `'no'`;
|
|
50
|
+
* **键缺席(`not_reported`)也是 `'no'`** —— 这一位的在场性本身就是版本信号,上游对缺席的成文语义是
|
|
51
|
+
* 「老 worker:本部署没有跨会话车道,按会话 id 投递按查无此会话拒」,所以缺席**不是**判不出,是一句
|
|
52
|
+
* 有内容的否定(与写键那一位「缺席 ⇒ 藏入口」同一条读法);只有本进程**从没观测过**(`unobserved`)
|
|
53
|
+
* 才是真的判不出 ⇒ `'unknown'`(别预判成有也别预判成没有)。
|
|
54
|
+
*/
|
|
55
|
+
export declare function peerLaneAvailable(reading: PeerLaneReading): 'yes' | 'no' | 'unknown';
|
|
56
|
+
/** doctor 那一行的 detail 串,唯一措辞真源。🔴 `unobserved` 那句不许暗示「有」或「没有」;`not_reported` 那句按上游成文语义说
|
|
57
|
+
* 「老 worker,没有跨会话车道」,并把「为什么缺席就是没有」(在场性即版本信号)说清,不让端把它读成第二种「判不出」。 */
|
|
58
|
+
export declare function peerLaneDoctorDetail(reading: PeerLaneReading): string;
|
|
59
|
+
/** 换代失效口(引擎温切成功后调):清成未观测。空串 ⇒ no-op;绝不 throw。 */
|
|
60
|
+
export declare function forgetPeerLaneReading(baseUrl: string | undefined): void;
|
|
61
|
+
/** 测试钩子。 */
|
|
62
|
+
export declare function __resetPeerLaneReadingsForTests(): void;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peerLaneCapability — `GET /v1/capabilities.peerLane`(server ≥7.91.1;sdk 9.8.1 尚未声明该键 ⇒ 结构读)
|
|
3
|
+
* 的三端共用读面(0.76.1 CC-102;与 sql / writeProtection / webSearch.backend / executionLane /
|
|
4
|
+
* approvalsStreamLive / deviceExecutor.management 各只兄弟同一套四态词汇)。
|
|
5
|
+
*
|
|
6
|
+
* ── 这一位答的是哪一个问题 ────────────────────────────────────────────────────────────────
|
|
7
|
+
* 「本部署的会话之间能不能互相发现并投递」——引擎的跨会话目录席是否已挂(会话枚举面 ∧ 耐久
|
|
8
|
+
* mailbox ∧ 该 mailbox 的收件人生命周期面,三缺一即缺席)。位为真时模型侧多出 `ListAgents`
|
|
9
|
+
* (列同一 principal 的其它会话)、`SendMessage` 的 `session.<id>` 地址臂,以及每 turn 边界对本
|
|
10
|
+
* 会话那只会话盒的排水。
|
|
11
|
+
*
|
|
12
|
+
* ── 🔴 本位不是 HTTP 面 ────────────────────────────────────────────────────────────────────
|
|
13
|
+
* 上游没有为这一族能力单开任何新端点 —— 会话清单仍走既有的会话列举口。所以本位的消费方式是
|
|
14
|
+
* **渲染 / 预期**(要不要露出跨会话的入口),不是拿它去 gate 某一个动词的调用。
|
|
15
|
+
* ⚠️ 辖域只到「席」:上游另有一道装配期挂载判据(mailbox 必须显式声明跨进程安全),不齐时会在
|
|
16
|
+
* 装配期响亮拒挂并通告运维;本读面刻意不复判那一道 —— 复判 = 给同一件事立第二个判官。
|
|
17
|
+
*
|
|
18
|
+
* ── 🔴 四态,不是两态([honest-absence-not-fabricated-zero])──────────────────────────────────
|
|
19
|
+
* ① `unobserved` —— 本进程一次 caps 响应都没观测到。
|
|
20
|
+
* ② `not_reported` —— caps 观测到了,而回体上**没有** `peerLane` 这个键 ⇒ 老 worker(比 7.91.1 老)。
|
|
21
|
+
* 这台部署有没有跨会话车道**判不出**,不是「没有」。
|
|
22
|
+
* ③ `absent` —— 键在、值**明确是** `false`:这台部署没有挂跨会话目录席。**正面事实**,
|
|
23
|
+
* 与 `not_reported`(判不出)是两件不同的事,处置也相反。
|
|
24
|
+
* ④ `present` —— 键在、值为 `true`:席已挂。
|
|
25
|
+
* 值既不是 `true` 也不是 `false`(串 / 数 / null / 数组 / 对象)⇒ 畸形 ⇒ 删格(⇒ `unobserved`)。
|
|
26
|
+
* 🔴 三种「没有」(`unobserved` / `not_reported` / `absent`)**不许互折** —— 折起来会把「判不出」
|
|
27
|
+
* 说成「引擎明说没有」,或把「一次都没问过」说成「问过、答案是没有」。
|
|
28
|
+
*/
|
|
29
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
30
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
31
|
+
/** caps 回体 → 本格读数;畸形一律 `undefined`(= 这一格不写 ⇒ 读口答 `unobserved`)。 */
|
|
32
|
+
export function projectPeerLaneCapability(caps) {
|
|
33
|
+
if (caps === null || typeof caps !== 'object')
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!('peerLane' in caps))
|
|
36
|
+
return { kind: 'not_reported' };
|
|
37
|
+
// 🔴 只读一次:在场判据与取值同一次读(变化的 getter 不会让两处判断看到两个不同的值)。
|
|
38
|
+
const v = caps.peerLane;
|
|
39
|
+
if (v === undefined)
|
|
40
|
+
return { kind: 'not_reported' };
|
|
41
|
+
if (typeof v !== 'boolean')
|
|
42
|
+
return undefined;
|
|
43
|
+
return v ? { kind: 'present' } : { kind: 'absent' };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 本格的 per-baseUrl 读账 + tee / 读口 / 失效口 / 测试钩四口 —— 能力位读器**共用同一份实现**
|
|
47
|
+
* ({@link createEngineCapReader});本文件只留这一格真正不同的部分:投影函数
|
|
48
|
+
* {@link projectPeerLaneCapability}、doctor 措辞表,以及本位自己的判据助手。
|
|
49
|
+
* 🔴 Map 在工厂闭包里,**一只读器一张**:收到读不懂的回体只删自己这一格,绝不连坐别的能力面。
|
|
50
|
+
*/
|
|
51
|
+
const reader = createEngineCapReader({
|
|
52
|
+
name: 'peerLane',
|
|
53
|
+
project: projectPeerLaneCapability,
|
|
54
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
55
|
+
});
|
|
56
|
+
/** 宿主 caps probe 的读面 tee 落点。绝不 throw;畸形 ⇒ 删格;`opts.generation` 关掉旧探测覆盖新读数的竞态。 */
|
|
57
|
+
export function noteEngineCapsForPeerLane(baseUrl, caps, opts) {
|
|
58
|
+
reader.note(baseUrl, caps, opts);
|
|
59
|
+
}
|
|
60
|
+
/** 本进程观测到的读数;这一格空缺 ⇒ `{kind:'unobserved'}`。 */
|
|
61
|
+
export function observedPeerLane(baseUrl = engineWireTarget()?.baseUrl) {
|
|
62
|
+
return reader.observed(baseUrl);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 「要不要在壳上露出跨会话入口(`ListAgents` / `SendMessage` 的 `session.<id>` 地址臂)」的判据
|
|
66
|
+
* 单源(三态):只有引擎明说挂了(`present`)才 `'yes'`;明说没挂(`absent`)⇒ `'no'`;
|
|
67
|
+
* **键缺席(`not_reported`)也是 `'no'`** —— 这一位的在场性本身就是版本信号,上游对缺席的成文语义是
|
|
68
|
+
* 「老 worker:本部署没有跨会话车道,按会话 id 投递按查无此会话拒」,所以缺席**不是**判不出,是一句
|
|
69
|
+
* 有内容的否定(与写键那一位「缺席 ⇒ 藏入口」同一条读法);只有本进程**从没观测过**(`unobserved`)
|
|
70
|
+
* 才是真的判不出 ⇒ `'unknown'`(别预判成有也别预判成没有)。
|
|
71
|
+
*/
|
|
72
|
+
export function peerLaneAvailable(reading) {
|
|
73
|
+
if (reading.kind === 'present')
|
|
74
|
+
return 'yes';
|
|
75
|
+
if (reading.kind === 'absent' || reading.kind === 'not_reported')
|
|
76
|
+
return 'no';
|
|
77
|
+
return 'unknown';
|
|
78
|
+
}
|
|
79
|
+
/** doctor 那一行的 detail 串,唯一措辞真源。🔴 `unobserved` 那句不许暗示「有」或「没有」;`not_reported` 那句按上游成文语义说
|
|
80
|
+
* 「老 worker,没有跨会话车道」,并把「为什么缺席就是没有」(在场性即版本信号)说清,不让端把它读成第二种「判不出」。 */
|
|
81
|
+
export function peerLaneDoctorDetail(reading) {
|
|
82
|
+
switch (reading.kind) {
|
|
83
|
+
case 'unobserved':
|
|
84
|
+
return 'cross-session lane not observed — the engine reports it on /v1/capabilities (peerLane); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
|
|
85
|
+
case 'not_reported':
|
|
86
|
+
return 'cross-session lane not reported by this engine — the engine predates the lane, so this deployment has no cross-session discovery or delivery (the key itself is the version signal; addressing another session by id is refused as unknown)';
|
|
87
|
+
case 'absent':
|
|
88
|
+
return 'no cross-session lane on this deployment — cross-session discovery and delivery (listing other sessions, addressing one by id) are unavailable';
|
|
89
|
+
case 'present':
|
|
90
|
+
return 'cross-session lane on — this engine can discover and deliver across sessions for the same caller';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** 换代失效口(引擎温切成功后调):清成未观测。空串 ⇒ no-op;绝不 throw。 */
|
|
94
|
+
export function forgetPeerLaneReading(baseUrl) {
|
|
95
|
+
reader.forget(baseUrl);
|
|
96
|
+
}
|
|
97
|
+
/** 测试钩子。 */
|
|
98
|
+
export function __resetPeerLaneReadingsForTests() {
|
|
99
|
+
reader.__resetForTests();
|
|
100
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* permissionRulesWriteCapability — `GET /v1/capabilities.permissionRulesWrite`(server ≥7.91.1;sdk 9.8.1
|
|
3
|
+
* 尚未声明该键 ⇒ 结构读)的三端共用读面(0.76.1 CC-102;与 sql / writeProtection / webSearch.backend /
|
|
4
|
+
* executionLane / approvalsStreamLive / deviceExecutor.management / peerLane 各只兄弟同一套四态词汇)。
|
|
5
|
+
*
|
|
6
|
+
* ── 这一位答的是哪一个问题 ────────────────────────────────────────────────────────────────
|
|
7
|
+
* 「收紧方向的单步规则写入口(把某条工具规则改成 deny/ask 的那一步)对**这一次调用方**够不够得着」。
|
|
8
|
+
* 这一位与「规则车道整体在不在」是上游同一个内部表达式的两次读——车道在场 ∧ 这次调用带得到可验明
|
|
9
|
+
* 的身份——所以两者永远同真同假;但它们**不是同一个位**,原因见下。
|
|
10
|
+
*
|
|
11
|
+
* ── 🔴 读法(两处上游源码逐字一致,别按撤销面那一位类推)─────────────────────────────────────
|
|
12
|
+
* ① **键缺席 ⇒ 这份二进制比单步写口本身还老**:更早的引擎上,规则车道的其它几口已经在正常
|
|
13
|
+
* 应答(甚至已经报真),但这一条写口压根没有路由——键缺席就是这件事的信号,消费端据此**藏起**
|
|
14
|
+
* 这个写入口(真打了也能认:那类拒绝是裸的「方法不许」形,但不必打了才知道)。
|
|
15
|
+
* ② **键在场 ⇒ 按这一位自己的值判「这次够不够得着」**:值为 `true`/`false` 直接就是答案,不需要
|
|
16
|
+
* 再去核对车道整体那一位。
|
|
17
|
+
* ③ ⚠️ **与撤销面那一只存在性信号刻意不同源**:那一位是**部署级**的、与调用方身份无关的布尔(它的
|
|
18
|
+
* 值本身要当版本探针用);本位不是——本位的值同时编码了「车道在不在」与「这次调用带没带得到
|
|
19
|
+
* 身份」两件事,上游明确说这两位的读法不能互相套用。
|
|
20
|
+
* ④ 位为真**不**保证任意一次写都成功:方向 / 规则文本 / scope 的合法性由每次调用自己的应答逐次
|
|
21
|
+
* 回答,这一位只回答「入口在不在、这次摸不摸得到」。
|
|
22
|
+
*
|
|
23
|
+
* ── 🔴 四态,不是两态([honest-absence-not-fabricated-zero])──────────────────────────────────
|
|
24
|
+
* ① `unobserved` —— 本进程一次 caps 响应都没观测到。
|
|
25
|
+
* ② `not_reported` —— caps 观测到了,而回体上**没有**这个键 ⇒ 引擎比这条写口老(见①),藏入口。
|
|
26
|
+
* ③ `absent` —— 键在、值**明确是** `false`:这次调用够不着这条写口(车道没开,或这次调用
|
|
27
|
+
* 带不到可验明的身份)。**正面事实**,与 `not_reported`(判不出)是两件不同的事。
|
|
28
|
+
* ④ `present` —— 键在、值为 `true`:这次调用摸得到这条写口。
|
|
29
|
+
* 值既不是 `true` 也不是 `false` ⇒ 畸形 ⇒ 删格(⇒ `unobserved`)。
|
|
30
|
+
* 🔴 三种「没有」(`unobserved` / `not_reported` / `absent`)**不许互折**。
|
|
31
|
+
*/
|
|
32
|
+
/** 四态读数。`unobserved` 由读口在这一格空缺时铸,不由投影铸。 */
|
|
33
|
+
export type PermissionRulesWriteReading = {
|
|
34
|
+
kind: 'unobserved';
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'not_reported';
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'absent';
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'present';
|
|
41
|
+
};
|
|
42
|
+
/** caps 回体 → 本格读数;畸形一律 `undefined`(= 这一格不写 ⇒ 读口答 `unobserved`)。 */
|
|
43
|
+
export declare function projectPermissionRulesWriteCapability(caps: unknown): PermissionRulesWriteReading | undefined;
|
|
44
|
+
/** 宿主 caps probe 的读面 tee 落点。绝不 throw;畸形 ⇒ 删格;`opts.generation` 关掉旧探测覆盖新读数的竞态。 */
|
|
45
|
+
export declare function noteEngineCapsForPermissionRulesWrite(baseUrl: string, caps: unknown, opts?: {
|
|
46
|
+
generation?: number;
|
|
47
|
+
}): void;
|
|
48
|
+
/** 本进程观测到的读数;这一格空缺 ⇒ `{kind:'unobserved'}`。 */
|
|
49
|
+
export declare function observedPermissionRulesWrite(baseUrl?: string | undefined): PermissionRulesWriteReading;
|
|
50
|
+
/**
|
|
51
|
+
* 「这次调用摸不摸得到单步写入口」的判据单源(布尔):只有 `present` 才 `true`;其余三态
|
|
52
|
+
* (`absent` / `not_reported` / `unobserved`)一律 `false`——藏入口的默认方向是**保守**的,
|
|
53
|
+
* 判不出与明确没有对「要不要露出这个写按钮」是同一个答案。
|
|
54
|
+
*
|
|
55
|
+
* 🔴 本函数是本位**唯一**的派生点:{@link "./hitl/persistedRulesWire.js".persistedRulesWriteAvailable}
|
|
56
|
+
* 只调用它,不再合取别的位、也不再另读一遍 caps——同一件事只许有一个判官。
|
|
57
|
+
*/
|
|
58
|
+
export declare function permissionRulesWriteAvailable(reading: PermissionRulesWriteReading): boolean;
|
|
59
|
+
/** doctor 那一行的 detail 串,唯一措辞真源。🔴 两种「读不出」都不许暗示「够得着」或「够不着」。 */
|
|
60
|
+
export declare function permissionRulesWriteDoctorDetail(reading: PermissionRulesWriteReading): string;
|
|
61
|
+
/** 换代失效口(引擎温切成功后调):清成未观测。空串 ⇒ no-op;绝不 throw。 */
|
|
62
|
+
export declare function forgetPermissionRulesWriteReading(baseUrl: string | undefined): void;
|
|
63
|
+
/** 测试钩子。 */
|
|
64
|
+
export declare function __resetPermissionRulesWriteReadingsForTests(): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* permissionRulesWriteCapability — `GET /v1/capabilities.permissionRulesWrite`(server ≥7.91.1;sdk 9.8.1
|
|
3
|
+
* 尚未声明该键 ⇒ 结构读)的三端共用读面(0.76.1 CC-102;与 sql / writeProtection / webSearch.backend /
|
|
4
|
+
* executionLane / approvalsStreamLive / deviceExecutor.management / peerLane 各只兄弟同一套四态词汇)。
|
|
5
|
+
*
|
|
6
|
+
* ── 这一位答的是哪一个问题 ────────────────────────────────────────────────────────────────
|
|
7
|
+
* 「收紧方向的单步规则写入口(把某条工具规则改成 deny/ask 的那一步)对**这一次调用方**够不够得着」。
|
|
8
|
+
* 这一位与「规则车道整体在不在」是上游同一个内部表达式的两次读——车道在场 ∧ 这次调用带得到可验明
|
|
9
|
+
* 的身份——所以两者永远同真同假;但它们**不是同一个位**,原因见下。
|
|
10
|
+
*
|
|
11
|
+
* ── 🔴 读法(两处上游源码逐字一致,别按撤销面那一位类推)─────────────────────────────────────
|
|
12
|
+
* ① **键缺席 ⇒ 这份二进制比单步写口本身还老**:更早的引擎上,规则车道的其它几口已经在正常
|
|
13
|
+
* 应答(甚至已经报真),但这一条写口压根没有路由——键缺席就是这件事的信号,消费端据此**藏起**
|
|
14
|
+
* 这个写入口(真打了也能认:那类拒绝是裸的「方法不许」形,但不必打了才知道)。
|
|
15
|
+
* ② **键在场 ⇒ 按这一位自己的值判「这次够不够得着」**:值为 `true`/`false` 直接就是答案,不需要
|
|
16
|
+
* 再去核对车道整体那一位。
|
|
17
|
+
* ③ ⚠️ **与撤销面那一只存在性信号刻意不同源**:那一位是**部署级**的、与调用方身份无关的布尔(它的
|
|
18
|
+
* 值本身要当版本探针用);本位不是——本位的值同时编码了「车道在不在」与「这次调用带没带得到
|
|
19
|
+
* 身份」两件事,上游明确说这两位的读法不能互相套用。
|
|
20
|
+
* ④ 位为真**不**保证任意一次写都成功:方向 / 规则文本 / scope 的合法性由每次调用自己的应答逐次
|
|
21
|
+
* 回答,这一位只回答「入口在不在、这次摸不摸得到」。
|
|
22
|
+
*
|
|
23
|
+
* ── 🔴 四态,不是两态([honest-absence-not-fabricated-zero])──────────────────────────────────
|
|
24
|
+
* ① `unobserved` —— 本进程一次 caps 响应都没观测到。
|
|
25
|
+
* ② `not_reported` —— caps 观测到了,而回体上**没有**这个键 ⇒ 引擎比这条写口老(见①),藏入口。
|
|
26
|
+
* ③ `absent` —— 键在、值**明确是** `false`:这次调用够不着这条写口(车道没开,或这次调用
|
|
27
|
+
* 带不到可验明的身份)。**正面事实**,与 `not_reported`(判不出)是两件不同的事。
|
|
28
|
+
* ④ `present` —— 键在、值为 `true`:这次调用摸得到这条写口。
|
|
29
|
+
* 值既不是 `true` 也不是 `false` ⇒ 畸形 ⇒ 删格(⇒ `unobserved`)。
|
|
30
|
+
* 🔴 三种「没有」(`unobserved` / `not_reported` / `absent`)**不许互折**。
|
|
31
|
+
*/
|
|
32
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
33
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
34
|
+
/** caps 回体 → 本格读数;畸形一律 `undefined`(= 这一格不写 ⇒ 读口答 `unobserved`)。 */
|
|
35
|
+
export function projectPermissionRulesWriteCapability(caps) {
|
|
36
|
+
if (caps === null || typeof caps !== 'object')
|
|
37
|
+
return undefined;
|
|
38
|
+
if (!('permissionRulesWrite' in caps))
|
|
39
|
+
return { kind: 'not_reported' };
|
|
40
|
+
// 🔴 只读一次:在场判据与取值同一次读。
|
|
41
|
+
const v = caps.permissionRulesWrite;
|
|
42
|
+
if (v === undefined)
|
|
43
|
+
return { kind: 'not_reported' };
|
|
44
|
+
if (typeof v !== 'boolean')
|
|
45
|
+
return undefined;
|
|
46
|
+
return v ? { kind: 'present' } : { kind: 'absent' };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 本格的 per-baseUrl 读账 + tee / 读口 / 失效口 / 测试钩四口 —— 能力位读器**共用同一份实现**
|
|
50
|
+
* ({@link createEngineCapReader});本文件只留这一格真正不同的部分:投影函数
|
|
51
|
+
* {@link projectPermissionRulesWriteCapability}、doctor 措辞表,以及本位自己的判据助手。
|
|
52
|
+
* 🔴 Map 在工厂闭包里,**一只读器一张**:收到读不懂的回体只删自己这一格,绝不连坐别的能力面。
|
|
53
|
+
*/
|
|
54
|
+
const reader = createEngineCapReader({
|
|
55
|
+
name: 'permissionRulesWrite',
|
|
56
|
+
project: projectPermissionRulesWriteCapability,
|
|
57
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
58
|
+
});
|
|
59
|
+
/** 宿主 caps probe 的读面 tee 落点。绝不 throw;畸形 ⇒ 删格;`opts.generation` 关掉旧探测覆盖新读数的竞态。 */
|
|
60
|
+
export function noteEngineCapsForPermissionRulesWrite(baseUrl, caps, opts) {
|
|
61
|
+
reader.note(baseUrl, caps, opts);
|
|
62
|
+
}
|
|
63
|
+
/** 本进程观测到的读数;这一格空缺 ⇒ `{kind:'unobserved'}`。 */
|
|
64
|
+
export function observedPermissionRulesWrite(baseUrl = engineWireTarget()?.baseUrl) {
|
|
65
|
+
return reader.observed(baseUrl);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 「这次调用摸不摸得到单步写入口」的判据单源(布尔):只有 `present` 才 `true`;其余三态
|
|
69
|
+
* (`absent` / `not_reported` / `unobserved`)一律 `false`——藏入口的默认方向是**保守**的,
|
|
70
|
+
* 判不出与明确没有对「要不要露出这个写按钮」是同一个答案。
|
|
71
|
+
*
|
|
72
|
+
* 🔴 本函数是本位**唯一**的派生点:{@link "./hitl/persistedRulesWire.js".persistedRulesWriteAvailable}
|
|
73
|
+
* 只调用它,不再合取别的位、也不再另读一遍 caps——同一件事只许有一个判官。
|
|
74
|
+
*/
|
|
75
|
+
export function permissionRulesWriteAvailable(reading) {
|
|
76
|
+
return reading.kind === 'present';
|
|
77
|
+
}
|
|
78
|
+
/** doctor 那一行的 detail 串,唯一措辞真源。🔴 两种「读不出」都不许暗示「够得着」或「够不着」。 */
|
|
79
|
+
export function permissionRulesWriteDoctorDetail(reading) {
|
|
80
|
+
switch (reading.kind) {
|
|
81
|
+
case 'unobserved':
|
|
82
|
+
return 'permission-rule write entry not observed — the engine reports it on /v1/capabilities (permissionRulesWrite); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
|
|
83
|
+
case 'not_reported':
|
|
84
|
+
return 'permission-rule write entry not reported by this engine — this binary predates the entry itself; hide it rather than calling it';
|
|
85
|
+
case 'absent':
|
|
86
|
+
return 'permission-rule write entry unreachable for this call — either the rules lane is not open on this deployment, or this call cannot bring a verifiable identity';
|
|
87
|
+
case 'present':
|
|
88
|
+
return 'permission-rule write entry reachable for this call — a per-call attempt still answers for itself whether the direction, text, and scope are accepted';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** 换代失效口(引擎温切成功后调):清成未观测。空串 ⇒ no-op;绝不 throw。 */
|
|
92
|
+
export function forgetPermissionRulesWriteReading(baseUrl) {
|
|
93
|
+
reader.forget(baseUrl);
|
|
94
|
+
}
|
|
95
|
+
/** 测试钩子。 */
|
|
96
|
+
export function __resetPermissionRulesWriteReadingsForTests() {
|
|
97
|
+
reader.__resetForTests();
|
|
98
|
+
}
|
|
@@ -160,8 +160,13 @@ export function stripSelfOrchestrationIntent(req) {
|
|
|
160
160
|
}
|
|
161
161
|
return out;
|
|
162
162
|
}
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
// 🔴 0.76.1 族扫收编(与主车道证明那半场同族、**处置刻意不同**):这两只也是「模块级对象当缺省值
|
|
164
|
+
// 往外交」的形,但它们的暴露面完全不同 —— `ownRead` 是本文件私有、结果只被读 `kind` / `value`、
|
|
165
|
+
// 一只都不出模块,所以「两个消费者按身份把两件事认成一件」这一半在这里不成立;剩下的唯一风险是
|
|
166
|
+
// 本模块将来有人往上写,而那正是冻结接得住的。⇒ 这里冻结代价为零、判别力刚好;主车道那半场是
|
|
167
|
+
// 交给宿主的公开输出面,只能靠**逐次新建**解别名(见 `adapt/ids.ts` 的 `mainLane`)。
|
|
168
|
+
const ABSENT = Object.freeze({ kind: 'absent' });
|
|
169
|
+
const ACCESSOR = Object.freeze({ kind: 'accessor' });
|
|
165
170
|
/**
|
|
166
171
|
* 从一个**不可信**对象上取一位:只认**自有数据描述符**的 `value`。
|
|
167
172
|
*
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
*/
|
|
44
44
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
45
45
|
import { engineWireTarget } from './engineWireTarget.js';
|
|
46
|
-
import {
|
|
46
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
47
47
|
/**
|
|
48
48
|
* caps 回体 → 本格读数;**畸形一律 `undefined`**(= 这一格不写 ⇒ 读口答 `unobserved`)。
|
|
49
49
|
*
|
|
@@ -80,7 +80,16 @@ export function projectSqlEngineCapability(caps) {
|
|
|
80
80
|
return undefined;
|
|
81
81
|
return { kind: 'present', view: { engine, isolation, txnMode } };
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* 本格的 per-baseUrl 读账 + tee / 读口 / 失效口 / 测试钩四口 —— 各只能力位读器**共用同一份实现**
|
|
85
|
+
* ({@link createEngineCapReader});本文件只留这一格真正不同的部分:投影函数 {@link projectSqlEngineCapability}、doctor 措辞表,以及本位自己的判据助手(如有)。
|
|
86
|
+
* 🔴 Map 在工厂闭包里,**一只读器一张**:收到读不懂的回体只删自己这一格,绝不连坐别的能力面。
|
|
87
|
+
*/
|
|
88
|
+
const reader = createEngineCapReader({
|
|
89
|
+
name: 'sql',
|
|
90
|
+
project: projectSqlEngineCapability,
|
|
91
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
92
|
+
});
|
|
84
93
|
/**
|
|
85
94
|
* 宿主 caps probe 的**读面 tee** 落点(壳侧与 mcpGate / sessionBackground / projectContext /
|
|
86
95
|
* crashConverged / workflowsGate 五条并列)。绝不 throw —— 读面腿不许反噬 caps 探测链。
|
|
@@ -103,32 +112,7 @@ const readingByBase = new Map();
|
|
|
103
112
|
* 有任何东西变坏。🔴 **tee 自己抓号是假判据**(陈旧那一发在这里读到的是新代号,永远像当代的)。
|
|
104
113
|
*/
|
|
105
114
|
export function noteEngineCapsForSqlEngine(baseUrl, caps, opts) {
|
|
106
|
-
|
|
107
|
-
return;
|
|
108
|
-
// 🔴 代际只在入口**快照一次**(异源对抗复审 R3):opts 是调用方的对象,回体上的 getter 能在投影中途改它;首末两次核对都用这份快照,
|
|
109
|
-
// 读 opts / 读代际抛出 ⇒ 不动格(与陈旧代际同一处置)。
|
|
110
|
-
const gen = snapshotCapsGeneration(opts);
|
|
111
|
-
if (gen === null)
|
|
112
|
-
return; // 调用方 opts 的 getter 抛出 = 供给坏了 ⇒ 不动格
|
|
113
|
-
if (!capsGenerationStillCurrent(baseUrl, gen))
|
|
114
|
-
return;
|
|
115
|
-
let reading;
|
|
116
|
-
try {
|
|
117
|
-
reading = projectSqlEngineCapability(caps);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
// 🔴 0.74.2 同形存量(CC-69 复审 R1 族扫):抛 getter / Proxy 的回体 = 畸形,与「投影答 undefined」同一处置 —— 删格,不许留着上一台的读数。
|
|
121
|
-
reading = undefined;
|
|
122
|
-
}
|
|
123
|
-
// 🔴 投影之后**再核一次代际**(异源对抗复审 R2):投影会执行回体上不可信的 getter,它可以在中途让这一格换代
|
|
124
|
-
// (invalidate + 新代际的 tee 已写入);此时无论投影返回还是抛出,都不许动新代际的格。
|
|
125
|
-
if (!capsGenerationStillCurrent(baseUrl, gen))
|
|
126
|
-
return;
|
|
127
|
-
if (reading === undefined) {
|
|
128
|
-
readingByBase.delete(baseUrl);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
readingByBase.set(baseUrl, reading);
|
|
115
|
+
reader.note(baseUrl, caps, opts);
|
|
132
116
|
}
|
|
133
117
|
/**
|
|
134
118
|
* 本进程观测到的 SQL 姿态;这一格空缺 ⇒ `{kind:'unobserved'}`(**绝不**折成 `none`)。
|
|
@@ -138,9 +122,7 @@ export function noteEngineCapsForSqlEngine(baseUrl, caps, opts) {
|
|
|
138
122
|
* ⇒ Node 壳上行为一字节不变,浏览器/桌面宿主则由 `installEngineWireTarget()` 显式装)。
|
|
139
123
|
*/
|
|
140
124
|
export function observedSqlEngine(baseUrl = engineWireTarget()?.baseUrl) {
|
|
141
|
-
|
|
142
|
-
return { kind: 'unobserved' };
|
|
143
|
-
return readingByBase.get(baseUrl) ?? { kind: 'unobserved' };
|
|
125
|
+
return reader.observed(baseUrl);
|
|
144
126
|
}
|
|
145
127
|
/** 单行 UNTRUSTED 座的展示上限(UTF-16 单元;与壳侧那份同值)。 */
|
|
146
128
|
const SQL_DETAIL_MAX = 40;
|
|
@@ -210,11 +192,9 @@ export function sqlEngineDoctorDetail(reading) {
|
|
|
210
192
|
* 空串 ⇒ no-op;绝不 throw(它跑在引擎温切路径上)。
|
|
211
193
|
*/
|
|
212
194
|
export function forgetSqlEngineReading(baseUrl) {
|
|
213
|
-
|
|
214
|
-
return;
|
|
215
|
-
readingByBase.delete(baseUrl);
|
|
195
|
+
reader.forget(baseUrl);
|
|
216
196
|
}
|
|
217
197
|
/** 测试钩子。 */
|
|
218
198
|
export function __resetSqlEngineReadingsForTests() {
|
|
219
|
-
|
|
199
|
+
reader.__resetForTests();
|
|
220
200
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
*/
|
|
39
39
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
40
40
|
import { engineWireTarget } from './engineWireTarget.js';
|
|
41
|
-
import {
|
|
41
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
42
42
|
/** `backend: "none"` —— 唯一带独立语义的词(见文件头);其余词按开集读。 */
|
|
43
43
|
export const WEB_SEARCH_BACKEND_NONE = 'none';
|
|
44
44
|
/**
|
|
@@ -66,7 +66,16 @@ export function projectWebSearchBackendCapability(caps) {
|
|
|
66
66
|
return { kind: 'none' };
|
|
67
67
|
return { kind: 'present', view: { backend } };
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* 本格的 per-baseUrl 读账 + tee / 读口 / 失效口 / 测试钩四口 —— 各只能力位读器**共用同一份实现**
|
|
71
|
+
* ({@link createEngineCapReader});本文件只留这一格真正不同的部分:投影函数 {@link projectWebSearchBackendCapability}、doctor 措辞表,以及本位自己的判据助手(如有)。
|
|
72
|
+
* 🔴 Map 在工厂闭包里,**一只读器一张**:收到读不懂的回体只删自己这一格,绝不连坐别的能力面。
|
|
73
|
+
*/
|
|
74
|
+
const reader = createEngineCapReader({
|
|
75
|
+
name: 'webSearch.backend',
|
|
76
|
+
project: projectWebSearchBackendCapability,
|
|
77
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
78
|
+
});
|
|
70
79
|
/**
|
|
71
80
|
* 宿主 caps probe 的**读面 tee** 落点(与 `noteEngineCapsForSqlEngine` / `noteEngineCapsForWriteProtection`
|
|
72
81
|
* 并列;宿主在同一处按序调三只)。绝不 throw —— 读面腿不许反噬 caps 探测链。
|
|
@@ -77,38 +86,11 @@ const readingByBase = new Map();
|
|
|
77
86
|
* 那一刻 `engineCapsGeneration(baseUrl)` 抓号、在这里原样交回,陈旧那一发整只丢掉;不传 ⇒ 照写。
|
|
78
87
|
*/
|
|
79
88
|
export function noteEngineCapsForWebSearchBackend(baseUrl, caps, opts) {
|
|
80
|
-
|
|
81
|
-
return;
|
|
82
|
-
// 🔴 代际只在入口**快照一次**(异源对抗复审 R3):opts 是调用方的对象,回体上的 getter 能在投影中途改它;首末两次核对都用这份快照,
|
|
83
|
-
// 读 opts / 读代际抛出 ⇒ 不动格(与陈旧代际同一处置)。
|
|
84
|
-
const gen = snapshotCapsGeneration(opts);
|
|
85
|
-
if (gen === null)
|
|
86
|
-
return; // 调用方 opts 的 getter 抛出 = 供给坏了 ⇒ 不动格
|
|
87
|
-
if (!capsGenerationStillCurrent(baseUrl, gen))
|
|
88
|
-
return;
|
|
89
|
-
let reading;
|
|
90
|
-
try {
|
|
91
|
-
reading = projectWebSearchBackendCapability(caps);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// 🔴 0.74.2 同形存量(CC-69 复审 R1 族扫):抛 getter / Proxy 的回体 = 畸形,与「投影答 undefined」同一处置 —— 删格,不许留着上一台的读数。
|
|
95
|
-
reading = undefined;
|
|
96
|
-
}
|
|
97
|
-
// 🔴 投影之后**再核一次代际**(异源对抗复审 R2):投影会执行回体上不可信的 getter,它可以在中途让这一格换代
|
|
98
|
-
// (invalidate + 新代际的 tee 已写入);此时无论投影返回还是抛出,都不许动新代际的格。
|
|
99
|
-
if (!capsGenerationStillCurrent(baseUrl, gen))
|
|
100
|
-
return;
|
|
101
|
-
if (reading === undefined) {
|
|
102
|
-
readingByBase.delete(baseUrl);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
readingByBase.set(baseUrl, reading);
|
|
89
|
+
reader.note(baseUrl, caps, opts);
|
|
106
90
|
}
|
|
107
91
|
/** 本进程观测到的部署默认后端;这一格空缺 ⇒ `{kind:'unobserved'}`(**绝不**折成 `none`)。 */
|
|
108
92
|
export function observedWebSearchBackend(baseUrl = engineWireTarget()?.baseUrl) {
|
|
109
|
-
|
|
110
|
-
return { kind: 'unobserved' };
|
|
111
|
-
return readingByBase.get(baseUrl) ?? { kind: 'unobserved' };
|
|
93
|
+
return reader.observed(baseUrl);
|
|
112
94
|
}
|
|
113
95
|
/** provider 词的展示上限(UTF-16 单元;与两只兄弟同值同理由 —— 开集词按 UNTRUSTED-for-display 处置)。 */
|
|
114
96
|
const WS_DETAIL_MAX = 40;
|
|
@@ -144,11 +126,9 @@ export function webSearchBackendDoctorDetail(reading) {
|
|
|
144
126
|
* 处置是**清成未观测**,不是留旧值也不是铸一个 `none`。空串 ⇒ no-op;绝不 throw。
|
|
145
127
|
*/
|
|
146
128
|
export function forgetWebSearchBackendReading(baseUrl) {
|
|
147
|
-
|
|
148
|
-
return;
|
|
149
|
-
readingByBase.delete(baseUrl);
|
|
129
|
+
reader.forget(baseUrl);
|
|
150
130
|
}
|
|
151
131
|
/** 测试钩子。 */
|
|
152
132
|
export function __resetWebSearchBackendReadingsForTests() {
|
|
153
|
-
|
|
133
|
+
reader.__resetForTests();
|
|
154
134
|
}
|