@sema-agent/client-core 0.30.3 → 0.30.5
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 +67 -0
- package/README.md +1 -1
- package/dist/env/localeGeo.d.ts +0 -40
- package/dist/env/localeGeo.js +11 -10
- package/dist/env/localeTag.d.ts +40 -0
- package/dist/env/localeTag.js +44 -0
- package/dist/env/uiLanguage.d.ts +18 -0
- package/dist/env/uiLanguage.js +37 -0
- package/dist/hitl/toolApprovalWire.d.ts +35 -1
- package/dist/hitl/toolApprovalWire.js +24 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +15 -0
- package/dist/model/providerCatalog.d.ts +65 -0
- package/dist/model/providerCatalog.js +74 -0
- package/dist/model/tierVocabulary.d.ts +43 -0
- package/dist/model/tierVocabulary.js +59 -0
- package/dist/subagent/engineSubagentTail.d.ts +20 -0
- package/dist/subagent/engineSubagentTail.js +38 -2
- package/docs/INTEGRATION-CLIENTS.md +11 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,73 @@
|
|
|
16
16
|
> 🔴 **互链**(web [C166]⑦):各版「已知局限」段只记**该版新增**;接入面已知局限的完整台账在
|
|
17
17
|
> `docs/INTEGRATION-CLIENTS.md` §6e/§7 —— **只读其一会漏**,两处都过。
|
|
18
18
|
|
|
19
|
+
## 0.30.5
|
|
20
|
+
|
|
21
|
+
- **#244 F4 族D:配置/模型表单源化(A-028.17/.18/.20 三件单源上收 + A-028.19 对账钉,additive,
|
|
22
|
+
零 BREAKING)**(2026-08-15;公开导出 707 → 724,+17 全 additive;常驻钉 = pure 门 F4D 段 35
|
|
23
|
+
checks + F4D-seat-catalog-pin 条件腿 4 checks(红先绿后:壳钉落地前该腿以断言红留证;codex
|
|
24
|
+
复审两处 [medium] 采纳 —— 腿剥注释锚初始化式防「删钉留注释」假绿、钉补精确等型轴防
|
|
25
|
+
readonly/any 漂移,后者以 d.ts 变异 readonly 化 → 壳 typecheck 红实证)):
|
|
26
|
+
- **件1 `model/providerCatalog.ts`(A-028.17)**:46 家 provider 表的**规范折表层** ——
|
|
27
|
+
`ProviderCatalogRow`(cli DirectoryRow 与 web 折表字段的严格超集)+ `providerCatalogRows` /
|
|
28
|
+
`providerCatalogCount` / `providerCatalogRowDetail` / `providerPresetById` / `providerApiShortName`。
|
|
29
|
+
此前这张表被 cli/web 折了三次(cli providerDirectory + web catalog.ts + web 降级表映射),
|
|
30
|
+
「host 怎么取 / 零模型家怎么说 / 顺序动不动」的判定各持一份靠人工对齐。#175 三条设计约束
|
|
31
|
+
逐字随迁(单一真源现算 / 全表不重排不过滤 / 诚实缺席「model id typed in」不编「0 models」)。
|
|
32
|
+
cli 已换装 re-export;web 第二份折表退役归 web 半场(发布帖点名)。
|
|
33
|
+
- **件2 `model/tierVocabulary.ts`(A-028.18)**:档位五档词表 + 派生 + 校验单源 —— `Tier` /
|
|
34
|
+
`TIER_ORDER` / `SURFACED_TIERS` / `CC_TIER_ALIASES` / `DEFAULT_TIER` / `TIER_BLURBS` /
|
|
35
|
+
`TierGroupDef` / `isTier` / `resolveTierBinding`(fail-open 沿链向下,镜像 core expandTiers)。
|
|
36
|
+
此前跨两仓共三份(cli tierCore + web tiers-page 词表 + web wording.ts 词表+派生)。settings
|
|
37
|
+
读写/归一/投影那半场仍归宿主(cli tierCore 只留存储)。
|
|
38
|
+
- **件3 `env/localeTag.ts` + `env/uiLanguage.ts`(A-028.20)**:locale tag **纯手术**单源
|
|
39
|
+
(`parseLocaleTag`:剥 codeset/modifier、中性 locale 认定、子标签切分)+ UI 语言词表与判定
|
|
40
|
+
(`UiLanguage` / `UI_LANGUAGES` / `UI_LANGUAGE_ENDONYMS` / `isUiLanguage` / `pickUiLanguage`,
|
|
41
|
+
自 cli i18n/core 整体迁入)。🔴 **双出口成文**(输出域裁定):同一台手术,两个判定各答各的
|
|
42
|
+
题 —— `pickUiLanguage` 答「界面用哪种文字」(en/zh,未知回落可读的 en),`resolveRegionHint`
|
|
43
|
+
答「哪个地址更可达」(cn/intl/unknown,未知绝不猜);`zh-Hant` 在前者是 zh、在后者是 intl,
|
|
44
|
+
是设计不是漂移。`localeGeo` 的 tag 手术改经同一单源(行为逐条保持,GEO 段 103 checks 未动)。
|
|
45
|
+
- **件4 SeatCatalogEntry 对账钉(A-028.19)**:`seatContract.ts` 头注自始声称「cli 侧以
|
|
46
|
+
satisfies 断言钉住两形不漂」而壳全仓 grep 0 命中(声明比现实多说)。修 = cli applyOnboard.ts
|
|
47
|
+
落真钉(双向 assignability + keyof 键集对轴,类型面零运行时逻辑)+ 本包 pure 门
|
|
48
|
+
`F4D-seat-catalog-pin` 条件腿反向钉「钉必须在场」(钉被删即红)。
|
|
49
|
+
- 门随批:portability index 闭包上限 128 → 132(四件纯叶逐件登记,零新外部包零 Node 内建);
|
|
50
|
+
typeshape unknown 出境 238 → 240(两条类型守卫 unknown 入参位,同族登记);singleton 名单
|
|
51
|
+
+7 常量表条目(localeGeo NEUTRAL_LOCALES 随手术迁 localeTag);INTEGRATION-CLIENTS §0a/§2/§2b
|
|
52
|
+
计数与域 11/14 行同批对齐。
|
|
53
|
+
|
|
54
|
+
## 0.30.4
|
|
55
|
+
|
|
56
|
+
- **#280 移交两件:probeCause 随卡透传 + tail meta 帧发布口(additive,零 BREAKING)**(2026-08-15;
|
|
57
|
+
红先绿后,常驻钉 = durable-card-display-keys ⑧ 段 10 checks + pure B7 probeCause 八钉 / B6
|
|
58
|
+
tail-meta 六钉(含 fail-soft 两钉:sink 同步抛错不撕裂 tail / async 拒绝有界观察零
|
|
59
|
+
unhandledRejection —— codex 复审 [high] 采纳);壳 #280 围栏 T6 现状锁候本批包透传落地
|
|
60
|
+
翻红接线):
|
|
61
|
+
- **件1 `ApprovalCardRequest.probeCause` 透传**(server ≥7.21.0-rc #253 的结构化探针因由,
|
|
62
|
+
`{code, roots:{shown,total}, further?}`):0.30.3 的两处「帧→卡入参」显式挑键把上游键丢弃 ——
|
|
63
|
+
活帧腿(`surfaceToolApprovalFrameAndRespond` 的卡入参)与 durable 行腿
|
|
64
|
+
(`surfaceFsApprovalAndDecide`,载体 = `PendingCheckpoint.riskDescriptor.probeCause`,server 对
|
|
65
|
+
riskDescriptor 整体透传与活帧同值)。本批两腿共用一把**载体形**判(`isProbeCauseCarrier`:
|
|
66
|
+
非 null 非数组对象)+ 原样搬运;🔴 内部结构(code/roots/further)**刻意不校** —— 结构校验归
|
|
67
|
+
呈卡端窄读器(壳 `probeCauseNote.readProbeCauseKey`),包只搬运,unknown 开集类型
|
|
68
|
+
(`ToolApprovalFrame.probeCause` / `ApprovalCardRequest.probeCause` 两位,agent-types 无现成形)。
|
|
69
|
+
帧键镜像 15→16 键;`probeCause` **领先** sdk 6.17.2 运行期锚一代 ⇒ 对账门 AHEAD_OF_ANCHOR
|
|
70
|
+
带退出条件登记(#144 persistedRuleShadowed 同形先例)。超集台账 `docs/type-superset.json`
|
|
71
|
+
补 `cc-counterpart-addition` 一条(CC 近邻 = `CanUseTool` options 的 `decisionReason`/
|
|
72
|
+
`blockedPath` 散文/标量位,差在结构化)。
|
|
73
|
+
- **件2 tail `event: meta` 帧发布口**(`engineSubagentTail.ts`):0.30.3 消费循环
|
|
74
|
+
`ev.event !== 'forward'` continue 把 meta 帧结构性 skip ⇒ `contentFrames` 供给形判别位
|
|
75
|
+
(server e439a6c,闭集 {on, progress_only, unknown} ——「内容永远不会来 vs 还没来」的唯一
|
|
76
|
+
诚实依据)到不了端。本批循环改帧分类闸(switch):meta 经新可装口
|
|
77
|
+
`installSubagentTailMetaSink`(载荷 `SubagentTailMeta = {taskId, meta}`,meta 为帧开集原文,
|
|
78
|
+
判别位窄读归消费端)发布后仍不进内容面;🔴 开闸范围**只放 meta**,heartbeat/未来非 forward
|
|
79
|
+
帧维持 skip。不装 sink = 0.30.3 现状字节不变。公面 +3 名
|
|
80
|
+
(`installSubagentTailMetaSink` 运行期导出 + `SubagentTailMeta`/`SubagentTailMetaSink` 类型),
|
|
81
|
+
baseline 706→707;singleton 台账补 `tailMetaSink`(high,ceiling 89→90 成文理由)。
|
|
82
|
+
- 已知局限(本版新增):meta 判别位的**消费臂在端上**(壳 #280 围栏 T6 翻红后接线);
|
|
83
|
+
`riskDescriptor.probeCause` 与 `shadowedRule` 同样不在 SDK `PendingCheckpoint.riskDescriptor`
|
|
84
|
+
声明里(类型半场候 SDK 班车,durable 腿为结构视图读)。
|
|
85
|
+
|
|
19
86
|
## 0.30.3
|
|
20
87
|
|
|
21
88
|
- **#244 F3 wire/会话判定上收 · 包半场(A-028.9/.10/.11/.12/.13/.15 族E;新三件
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
35
35
|
|
|
36
36
|
## Scope
|
|
37
37
|
|
|
38
|
-
**Version:** 0.30.
|
|
38
|
+
**Version:** 0.30.5
|
|
39
39
|
|
|
40
40
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
41
41
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
package/dist/env/localeGeo.d.ts
CHANGED
|
@@ -1,43 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* localeGeo.ts — **地域预选 v1(零网络)**:只看系统 locale 与 IANA 时区,给出一个「默认选哪个
|
|
3
|
-
* 地址更可达」的提示。三端复用(CLI / web / 桌面)。
|
|
4
|
-
*
|
|
5
|
-
* ## 🔴 零耦合约束:模型 provider 目录不吃地域信号(clay 令 2026-07-31)
|
|
6
|
-
*
|
|
7
|
-
* 模型 provider **不分国内国外,完全用户选择,走全表** —— 模型 provider 表恒**全表呈现、顺序
|
|
8
|
-
* 不随地域变**,选哪家 100% 是用户的事。因此本模块与模型目录之间是**设计上的零耦合**,不是巧合:
|
|
9
|
-
* · 本模块**不导出**任何 `sortProvidersByRegion` / `filterProvidersFor(region)` 形状的函数;
|
|
10
|
-
* · `src/model/**` **不许** import 本模块(pure 门 GEO 段有机械断言把这条钉住,不靠自觉);
|
|
11
|
-
* · 唯一合法用途:**搜索 provider / 镜像源 / SearXNG 镜像地址**这类「取哪个地址更可达」的
|
|
12
|
-
* **默认预选**,而且**永远只是默认值,用户可改**。
|
|
13
|
-
*
|
|
14
|
-
* ## 为什么 v1 零网络
|
|
15
|
-
*
|
|
16
|
-
* 拨号探测(测 RTT / 查出口 IP)会在**用户还没同意任何事**之前就产生一次出站请求,而它换来的
|
|
17
|
-
* 只是一个初始默认值。locale 与时区是宿主已经知道的事实,不需要问任何人。⇒ 本模块是纯函数,
|
|
18
|
-
* 输入由调用方注入(不读宿主的环境变量,与本包 hostEnv 纪律一致:环境是宿主资产)。
|
|
19
|
-
* 门里那条「零环境读取」断言查的是**整份源文件**(注释也算),所以本文里刻意不出现那个成员
|
|
20
|
-
* 表达式的字面写法 —— 判据不必先长出一个词法器,代价只是这一行说明。
|
|
21
|
-
*
|
|
22
|
-
* ## 三档,`unknown` 不是 `intl`
|
|
23
|
-
*
|
|
24
|
-
* 两个信号都没有 ⇒ `unknown`,**绝不猜 `intl`**。「不知道」和「知道它在境外」是两件事:端拿到
|
|
25
|
-
* `unknown` 应当**不预选**(让用户自己挑),拿到 `intl` 才是预选国际地址。把不知道渲染成一个
|
|
26
|
-
* 具体答案,就是[honest-absence-not-fabricated-zero]里那条「编造零值」的同族错误。
|
|
27
|
-
*
|
|
28
|
-
* ## 冲突时**时区赢**(locale 说 zh 但时区是 America/New_York)
|
|
29
|
-
*
|
|
30
|
-
* 理由锚在本模块的用途上 —— 判的是「**哪个地址更可达**」,那是**机器在哪张网上**决定的:
|
|
31
|
-
* · **时区** = 系统对自己**所在位置**的记录(装机/NTP 时按位置设),与网络出口高度相关;
|
|
32
|
-
* · **locale** = 用户偏好**哪种语言**,与机器在哪张网上无关(在纽约用中文界面的人很多)。
|
|
33
|
-
* ⇒ 时区是位置的更强证据,冲突时它赢。两个信号一致时 `reason` 同时点名两者(证据更足)。
|
|
34
|
-
*
|
|
35
|
-
* ## `reason` 是「我看到了什么」,不是「你在哪」
|
|
36
|
-
*
|
|
37
|
-
* 端会把它渲成「已按你的系统区域预选,可随时改」。所以 reason 只陈述观测到的事实
|
|
38
|
-
* (`system time zone Asia/Shanghai`),不做身份断言(不写 "you are in China")—— 用户看到的
|
|
39
|
-
* 是判据本身,于是「这判据不对」是他能当场看出来并改掉的。
|
|
40
|
-
*/
|
|
41
1
|
/** 三档。🔴 `unknown` ≠ `intl`:前者是没有判据,后者是有判据且指向境外。 */
|
|
42
2
|
export type RegionHint = 'cn' | 'intl' | 'unknown';
|
|
43
3
|
/** 全部输入由调用方注入(Node 侧传 `env.LANG`/`env.LC_ALL` 与 `Intl.DateTimeFormat().resolvedOptions().timeZone`)。 */
|
package/dist/env/localeGeo.js
CHANGED
|
@@ -37,7 +37,14 @@
|
|
|
37
37
|
* 端会把它渲成「已按你的系统区域预选,可随时改」。所以 reason 只陈述观测到的事实
|
|
38
38
|
* (`system time zone Asia/Shanghai`),不做身份断言(不写 "you are in China")—— 用户看到的
|
|
39
39
|
* 是判据本身,于是「这判据不对」是他能当场看出来并改掉的。
|
|
40
|
+
*
|
|
41
|
+
* ## tag 手术委托 `./localeTag.js`(F4 族D A-028.20,双出口成文)
|
|
42
|
+
*
|
|
43
|
+
* 剥 codeset/modifier、中性 locale 认定、子标签切分这台**手术**与 `env/uiLanguage.ts` 共用
|
|
44
|
+
* 同一份实现;但两件各答各的题(本件答「哪个地址更可达」,那件答「界面用哪种文字」),
|
|
45
|
+
* 输出域语义不同,刻意不收敛 —— 理由成文在 localeTag.ts 文件头。
|
|
40
46
|
*/
|
|
47
|
+
import { parseLocaleTag } from './localeTag.js';
|
|
41
48
|
/**
|
|
42
49
|
* 判为「内地」的 IANA 时区(含 backward 链接别名与老 `PRC` 形)。
|
|
43
50
|
* 🔴 `Asia/Hong_Kong` / `Asia/Macau` / `Asia/Taipei` **不在**表内:本模块判的是「哪个镜像地址
|
|
@@ -54,10 +61,6 @@ const CN_TIME_ZONES = new Set([
|
|
|
54
61
|
]);
|
|
55
62
|
/** IANA 时区名的形(`Area/Location`,允许多级如 `America/Argentina/Salta`)。 */
|
|
56
63
|
const IANA_SHAPE = /^[a-z]+(?:\/[a-z0-9_+-]+)+$/i;
|
|
57
|
-
/** 语言标签的形:头一段是 2–3 位字母的语言码。不合形的串(`1234` / 空串)当没信号。 */
|
|
58
|
-
const LANGUAGE_TAG_SHAPE = /^[a-z]{2,3}(?:[-_]|$)/i;
|
|
59
|
-
/** 显式「无 locale」的 POSIX 值 —— 它们不携带任何地域信息。 */
|
|
60
|
-
const NEUTRAL_LOCALES = new Set(['c', 'posix']);
|
|
61
64
|
/** 不含地理信息的时区(UTC 家族):有值,但不构成判据。 */
|
|
62
65
|
const GEOLESS_ZONES = new Set(['utc', 'gmt', 'z', 'universal', 'zulu']);
|
|
63
66
|
/** reason 里回显观测值的长度上限 —— 输入来自宿主环境变量,不该由它决定一行 UI 有多长。 */
|
|
@@ -73,11 +76,9 @@ const echo = (v) => (v.length > ECHO_MAX ? `${v.slice(0, ECHO_MAX)}…` : v);
|
|
|
73
76
|
function localeTag(input) {
|
|
74
77
|
const pick = (v) => (typeof v === 'string' ? v.trim() : '');
|
|
75
78
|
const raw = pick(input.lcAll) || pick(input.lang);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return '';
|
|
80
|
-
return LANGUAGE_TAG_SHAPE.test(tag) ? tag : '';
|
|
79
|
+
// tag 手术(剥 codeset/modifier、中性 locale、认形)单源在 localeTag.ts —— 本件只保留
|
|
80
|
+
// 「LC_ALL 独赢」这半句 POSIX 语义,手术本身不再自持第二份。
|
|
81
|
+
return parseLocaleTag(raw).tag;
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
84
|
* locale → 信号。`zh` 系按**地区码优先、脚本次之**读(地区码比脚本更接近地理事实:
|
|
@@ -86,7 +87,7 @@ function localeTag(input) {
|
|
|
86
87
|
function localeSignal(tag) {
|
|
87
88
|
if (!tag)
|
|
88
89
|
return 'unknown';
|
|
89
|
-
const parts = tag
|
|
90
|
+
const parts = parseLocaleTag(tag).subtags;
|
|
90
91
|
if (parts[0] !== 'zh')
|
|
91
92
|
return 'intl'; // 非中文 locale = 有信号且不指向内地
|
|
92
93
|
const rest = parts.slice(1);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* localeTag.ts — F4 族D(A-028.20,2026-08-15):locale 标签的**纯 tag 手术**单源。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么要有这一件
|
|
5
|
+
*
|
|
6
|
+
* 「把一个 locale 标签剥成语言子标签」这台手术此前被做了三次:cli `i18n/core/uiLanguage.ts`
|
|
7
|
+
* (剥 codeset/modifier → 取语言子标签 → 判 UI 语言)、本包 `env/localeGeo.ts`(同一台手术 →
|
|
8
|
+
* 判地域预选)、web 侧第二份(答案还不一致)。手术步骤漂了(比如谁剥 `@modifier` 谁不剥),
|
|
9
|
+
* 同一个 `zh_CN.UTF-8@pinyin` 会在不同端得出不同判定。手术进包单源后,两个**输出域不同**的
|
|
10
|
+
* 判定(见下)共用这一台手术,步骤从结构上不可能再漂。
|
|
11
|
+
*
|
|
12
|
+
* ## 🔴 双出口成文:tag 手术单源 ≠ 答案单源
|
|
13
|
+
*
|
|
14
|
+
* 本件只做**手术**(剥、切、认形),不做**判定**。判定有两个,输出域**语义不同**,刻意不收敛:
|
|
15
|
+
* · `env/uiLanguage.ts` `pickUiLanguage` → `'en' | 'zh'`:**用户读哪种界面文字**(语言偏好域;
|
|
16
|
+
* 未知回落 en = 回落到可读的 fallback 目录);
|
|
17
|
+
* · `env/localeGeo.ts` `resolveRegionHint` → `'cn' | 'intl' | 'unknown'`:**机器在哪张网上**
|
|
18
|
+
* (地址可达域;未知就是 unknown,绝不猜 —— 端应当不预选)。
|
|
19
|
+
* 两域对同一输入给不同答案是**设计**,不是漂移:`zh-Hant` 的界面语言是中文(zh),而它的网络
|
|
20
|
+
* 预选是境外(intl)。谁要「收敛成一个函数」谁就把这两件事重新搅在一起。
|
|
21
|
+
*
|
|
22
|
+
* ## 手术步骤(POSIX `zh_CN.UTF-8@pinyin` → `zh_CN`)
|
|
23
|
+
*
|
|
24
|
+
* ① trim;② 剥 codeset(`.` 后);③ 剥 modifier(`@` 后)再 trim;
|
|
25
|
+
* ④ 中性 locale(`C`/`POSIX`)与不合语言标签形的串(头段非 2–3 位字母)⇒ **无信号**
|
|
26
|
+
* (`tag: ''`)——「没有 locale」和「locale 指向某语言」必须分得开;
|
|
27
|
+
* ⑤ 子标签统一小写、`_`→`-` 后切开(`tag` 保留原大小写 —— 端要拿它回显观测值)。
|
|
28
|
+
*/
|
|
29
|
+
/** 手术产物。`tag === ''` = 无信号(空/中性/形不对),此时 language/subtags 也为空。 */
|
|
30
|
+
export interface ParsedLocaleTag {
|
|
31
|
+
/** 剥 codeset/modifier 后的标签**原样**(保留大小写,供端回显观测值);'' = 无信号。 */
|
|
32
|
+
tag: string;
|
|
33
|
+
/** 语言子标签(小写);无信号时 ''。 */
|
|
34
|
+
language: string;
|
|
35
|
+
/** 全部子标签(小写,[-_] 切);无信号时 []。 */
|
|
36
|
+
subtags: string[];
|
|
37
|
+
}
|
|
38
|
+
/** 纯 tag 手术(见文件头步骤表)。任何宿主形态的标签都吃:POSIX `LANG`、BCP 47、
|
|
39
|
+
* `navigator.language`、Electron `app.getLocale()`。 */
|
|
40
|
+
export declare function parseLocaleTag(raw: string | undefined): ParsedLocaleTag;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* localeTag.ts — F4 族D(A-028.20,2026-08-15):locale 标签的**纯 tag 手术**单源。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么要有这一件
|
|
5
|
+
*
|
|
6
|
+
* 「把一个 locale 标签剥成语言子标签」这台手术此前被做了三次:cli `i18n/core/uiLanguage.ts`
|
|
7
|
+
* (剥 codeset/modifier → 取语言子标签 → 判 UI 语言)、本包 `env/localeGeo.ts`(同一台手术 →
|
|
8
|
+
* 判地域预选)、web 侧第二份(答案还不一致)。手术步骤漂了(比如谁剥 `@modifier` 谁不剥),
|
|
9
|
+
* 同一个 `zh_CN.UTF-8@pinyin` 会在不同端得出不同判定。手术进包单源后,两个**输出域不同**的
|
|
10
|
+
* 判定(见下)共用这一台手术,步骤从结构上不可能再漂。
|
|
11
|
+
*
|
|
12
|
+
* ## 🔴 双出口成文:tag 手术单源 ≠ 答案单源
|
|
13
|
+
*
|
|
14
|
+
* 本件只做**手术**(剥、切、认形),不做**判定**。判定有两个,输出域**语义不同**,刻意不收敛:
|
|
15
|
+
* · `env/uiLanguage.ts` `pickUiLanguage` → `'en' | 'zh'`:**用户读哪种界面文字**(语言偏好域;
|
|
16
|
+
* 未知回落 en = 回落到可读的 fallback 目录);
|
|
17
|
+
* · `env/localeGeo.ts` `resolveRegionHint` → `'cn' | 'intl' | 'unknown'`:**机器在哪张网上**
|
|
18
|
+
* (地址可达域;未知就是 unknown,绝不猜 —— 端应当不预选)。
|
|
19
|
+
* 两域对同一输入给不同答案是**设计**,不是漂移:`zh-Hant` 的界面语言是中文(zh),而它的网络
|
|
20
|
+
* 预选是境外(intl)。谁要「收敛成一个函数」谁就把这两件事重新搅在一起。
|
|
21
|
+
*
|
|
22
|
+
* ## 手术步骤(POSIX `zh_CN.UTF-8@pinyin` → `zh_CN`)
|
|
23
|
+
*
|
|
24
|
+
* ① trim;② 剥 codeset(`.` 后);③ 剥 modifier(`@` 后)再 trim;
|
|
25
|
+
* ④ 中性 locale(`C`/`POSIX`)与不合语言标签形的串(头段非 2–3 位字母)⇒ **无信号**
|
|
26
|
+
* (`tag: ''`)——「没有 locale」和「locale 指向某语言」必须分得开;
|
|
27
|
+
* ⑤ 子标签统一小写、`_`→`-` 后切开(`tag` 保留原大小写 —— 端要拿它回显观测值)。
|
|
28
|
+
*/
|
|
29
|
+
/** 显式「无 locale」的 POSIX 值 —— 它们不携带任何语言/地域信息。 */
|
|
30
|
+
const NEUTRAL_LOCALES = new Set(['c', 'posix']);
|
|
31
|
+
/** 语言标签的形:头一段是 2–3 位字母的语言码。不合形的串(`1234` / 空串)当没信号。 */
|
|
32
|
+
const LANGUAGE_TAG_SHAPE = /^[a-z]{2,3}(?:[-_]|$)/i;
|
|
33
|
+
/** 纯 tag 手术(见文件头步骤表)。任何宿主形态的标签都吃:POSIX `LANG`、BCP 47、
|
|
34
|
+
* `navigator.language`、Electron `app.getLocale()`。 */
|
|
35
|
+
export function parseLocaleTag(raw) {
|
|
36
|
+
const trimmed = typeof raw === 'string' ? raw.trim() : '';
|
|
37
|
+
const noCodeset = trimmed.split('.')[0] ?? '';
|
|
38
|
+
const tag = (noCodeset.split('@')[0] ?? '').trim();
|
|
39
|
+
if (!tag || NEUTRAL_LOCALES.has(tag.toLowerCase()) || !LANGUAGE_TAG_SHAPE.test(tag)) {
|
|
40
|
+
return { tag: '', language: '', subtags: [] };
|
|
41
|
+
}
|
|
42
|
+
const subtags = tag.toLowerCase().replace(/_/g, '-').split('-');
|
|
43
|
+
return { tag, language: subtags[0] ?? '', subtags };
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The languages sema's own screens are available in. */
|
|
2
|
+
export type UiLanguage = 'en' | 'zh';
|
|
3
|
+
/** Menu order: the detected language is pre-focused, so this order is presentation only. */
|
|
4
|
+
export declare const UI_LANGUAGES: readonly UiLanguage[];
|
|
5
|
+
/**
|
|
6
|
+
* Language names in the language itself (endonyms). NEVER translated and never routed through the
|
|
7
|
+
* message catalog: a language picker has to be readable by someone who cannot read the current UI
|
|
8
|
+
* language — that is the whole point of the screen.
|
|
9
|
+
*/
|
|
10
|
+
export declare const UI_LANGUAGE_ENDONYMS: Record<UiLanguage, string>;
|
|
11
|
+
export declare function isUiLanguage(v: unknown): v is UiLanguage;
|
|
12
|
+
/**
|
|
13
|
+
* Decide the UI language from ONE locale tag, whatever the host got it from — a POSIX `LANG`
|
|
14
|
+
* (`zh_CN.UTF-8`), a BCP 47 tag (`zh-Hant`), `navigator.language`, an Electron `app.getLocale()`.
|
|
15
|
+
* Anything whose language subtag is `zh` ⇒ 中文; everything else — including empty, `C` and
|
|
16
|
+
* `POSIX` — ⇒ English (which is also the fallback catalog, so "unknown" degrades to "readable").
|
|
17
|
+
*/
|
|
18
|
+
export declare function pickUiLanguage(localeTag: string | undefined): UiLanguage;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* uiLanguage.ts — F4 族D(A-028.20,2026-08-15;整体自 cli `src/sema/i18n/core/uiLanguage.ts`
|
|
3
|
+
* 搬迁):UI 语言的**词表 + 判定**——类型、两种受支持语言、各自的 endonym、以及
|
|
4
|
+
* 「一个 locale 标签 → UI 语言」的纯判定。三端(CLI / web / 桌面)共用这一份。
|
|
5
|
+
*
|
|
6
|
+
* tag 手术(剥 codeset/modifier、切子标签)委托 `./localeTag.js` —— 与 `./localeGeo.js` 共用
|
|
7
|
+
* **同一台手术**但**各答各的题**(双出口成文,理由见 localeTag.ts 文件头:语言偏好域 ≠ 地址
|
|
8
|
+
* 可达域,`zh-Hant` 在前者是 zh、在后者是 intl)。
|
|
9
|
+
*
|
|
10
|
+
* 标签从哪来是宿主的事,本件不读环境:CLI 读 POSIX env(LC_ALL > LC_MESSAGES > LANG),
|
|
11
|
+
* 浏览器读 `navigator.language`,Electron 读 `app.getLocale()` —— 各宿主的三行 adapter 都
|
|
12
|
+
* 调同一个 `pickUiLanguage`。
|
|
13
|
+
*/
|
|
14
|
+
import { parseLocaleTag } from './localeTag.js';
|
|
15
|
+
/** Menu order: the detected language is pre-focused, so this order is presentation only. */
|
|
16
|
+
export const UI_LANGUAGES = ['en', 'zh'];
|
|
17
|
+
/**
|
|
18
|
+
* Language names in the language itself (endonyms). NEVER translated and never routed through the
|
|
19
|
+
* message catalog: a language picker has to be readable by someone who cannot read the current UI
|
|
20
|
+
* language — that is the whole point of the screen.
|
|
21
|
+
*/
|
|
22
|
+
export const UI_LANGUAGE_ENDONYMS = {
|
|
23
|
+
en: 'English',
|
|
24
|
+
zh: '中文',
|
|
25
|
+
};
|
|
26
|
+
export function isUiLanguage(v) {
|
|
27
|
+
return v === 'en' || v === 'zh';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Decide the UI language from ONE locale tag, whatever the host got it from — a POSIX `LANG`
|
|
31
|
+
* (`zh_CN.UTF-8`), a BCP 47 tag (`zh-Hant`), `navigator.language`, an Electron `app.getLocale()`.
|
|
32
|
+
* Anything whose language subtag is `zh` ⇒ 中文; everything else — including empty, `C` and
|
|
33
|
+
* `POSIX` — ⇒ English (which is also the fallback catalog, so "unknown" degrades to "readable").
|
|
34
|
+
*/
|
|
35
|
+
export function pickUiLanguage(localeTag) {
|
|
36
|
+
return parseLocaleTag(localeTag).language === 'zh' ? 'zh' : 'en';
|
|
37
|
+
}
|
|
@@ -238,6 +238,18 @@ export interface ApprovalCardRequest {
|
|
|
238
238
|
* 压根没有规则命中(缺席),而命中且清掉了的那些根本不会变成卡。
|
|
239
239
|
*/
|
|
240
240
|
persistedRuleShadowed?: string;
|
|
241
|
+
/**
|
|
242
|
+
* 结构化探针因由(#280 件1,0.30.4;server ≥7.21.0-rc #253)——**双源合流**
|
|
243
|
+
* ({@link governanceForced} 同形,两腿在场性可以不一致):
|
|
244
|
+
* · **活卡帧腿**:{@link ToolApprovalFrame.probeCause} 原样;
|
|
245
|
+
* · **durable park 行腿**:`PendingCheckpoint.riskDescriptor.probeCause`(server 对
|
|
246
|
+
* riskDescriptor 整体透传,与活帧同值 —— core `buildRiskDescriptor` 铸)。
|
|
247
|
+
* 🔴 **开集(unknown),包只搬运**:载体形判(非 null 非数组对象)之外零校验零改写 ——
|
|
248
|
+
* 内部结构(code/roots/further)的窄读、消毒与呈现全部归端(壳 `probeCauseNote` 窄读器:
|
|
249
|
+
* 形不合整只按缺席,呈现走 wireNote 车道)。缺席 = 无因由/老引擎/坏载体形(三者同形,不猜),
|
|
250
|
+
* 卡形字节不变。UNTRUSTED-for-display:code/roots 是工具作文面,只渲染绝不回喂模型/工具入参。
|
|
251
|
+
*/
|
|
252
|
+
probeCause?: unknown;
|
|
241
253
|
}
|
|
242
254
|
/**
|
|
243
255
|
* 🔴 **拆缝口** —— 弹「三选卡」并等人的决断。壳 = vendored CC `PermissionRequest`;
|
|
@@ -352,6 +364,28 @@ export interface ToolApprovalFrame {
|
|
|
352
364
|
* 耐久对偶仍未消费——那一路是独立的一件,不在本键的施工面内。
|
|
353
365
|
*/
|
|
354
366
|
persistedRuleShadowed?: string;
|
|
367
|
+
/**
|
|
368
|
+
* server ≥7.21.0-rc(#253 件 G1,core 5.33.0 backlog #239;**ADDITIVE**,`"tool_approval"` only。
|
|
369
|
+
* 真发直证 = engine 7.22.0 fixture `@sema-agent/server/dist/tool-approval.d.ts` 同名键)——
|
|
370
|
+
* 这只 ask **为什么**被收紧的**结构化探针因由**:被拦工具自声明 `reversibilityProbe` 且判不出
|
|
371
|
+
* 可回滚时,引擎在 maybe 档收紧点铸;wire 契约形 = `{ code, roots:{shown,total},
|
|
372
|
+
* further?:{shown,total} }`(server `ProbeCauseSchema` `.strict()`,活卡帧与 durable `card_json`
|
|
373
|
+
* 经同一 `readProbeCause` 单点铸造 ⇒ 两面结构性同值)。
|
|
374
|
+
*
|
|
375
|
+
* 🔴 **本包刻意不拥有这个形**(类型 = unknown 开集;#280 件1,0.30.4):内部结构(code/roots/
|
|
376
|
+
* further)的校验与呈现归呈卡端的窄读器(壳 `probeCauseNote.readProbeCauseKey` 同款「形不合整只
|
|
377
|
+
* 按缺席」纪律),包只做**载体形**判(非 null 非数组对象)+ 原样搬运 —— 两层各校一遍会让
|
|
378
|
+
* 「谁把键窄没的」在排障时说不清,且包侧校严一档就把上游 additive 内键挡在边界外。
|
|
379
|
+
* 🔴 **缺席 ≠「没有原因」**:只有 maybe 档收紧且探针真给了 cause 的 ask 才有它(工具 opt-in
|
|
380
|
+
* 供给,非常驻字段 —— [3980] test 定谳的负控:mandate 路径 ask 帧不带)。
|
|
381
|
+
* 🔴 本键**领先** SDK 运行期锚一代(sdk 6.17.2 的 `TOOL_APPROVAL_FRAME_KEYS` 尚无)⇒ 对账门
|
|
382
|
+
* (run-approval-frame-keys-test.mjs)AHEAD_OF_ANCHOR 带退出条件登记,#144
|
|
383
|
+
* `persistedRuleShadowed` 同形先例:SDK 锚补上当天登记自红逼删。
|
|
384
|
+
* 耐久路对偶 = `PendingCheckpoint.riskDescriptor.probeCause`(同值;server 对 riskDescriptor
|
|
385
|
+
* 整体透传),由 {@link surfaceFsApprovalAndDecide} 的行 → 卡重铸处消费。
|
|
386
|
+
* UNTRUSTED-for-display:code/roots 是工具作文面(server 已 redact),端呈前消毒,只渲染绝不回喂。
|
|
387
|
+
*/
|
|
388
|
+
probeCause?: unknown;
|
|
355
389
|
}
|
|
356
390
|
/** {@link ToolApprovalFrame.delegation} 的形(命名形,不用内联匿名 —— typeshape 门 B4 棘轮口径)。 */
|
|
357
391
|
export interface ToolApprovalDelegation {
|
|
@@ -367,7 +401,7 @@ export interface ToolApprovalDelegation {
|
|
|
367
401
|
* `TOOL_APPROVAL_FRAME_KEYS` 比对——SDK additive 增键时对账当天红,不再人肉追平。
|
|
368
402
|
* 下面两个类型钉保证镜像与 interface 本身不可能漂移(少键/多键都是编译错)。
|
|
369
403
|
*/
|
|
370
|
-
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "persistedRuleShadowed", "delegation", "outcome"];
|
|
404
|
+
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "persistedRuleShadowed", "probeCause", "delegation", "outcome"];
|
|
371
405
|
/** 子代帧判别:显式键 fromSubagent(core 1.378 RB-39②)优先;缺席退 sourceTaskId 在场性权宜式
|
|
372
406
|
* (server 1.258 [1549]①3,旧代际兼容)。 */
|
|
373
407
|
export declare function isFromSubagent(frame: ToolApprovalFrame): boolean;
|
|
@@ -255,6 +255,11 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
255
255
|
// 合形窄化,落**只读键**(红线见 ApprovalCardRequest.ruleSuggestionsReadOnly 顶注:/decide
|
|
256
256
|
// 无规则位,落可兑付位=假 affordance)。
|
|
257
257
|
const ruleSuggestionsReadOnly = readRuleSuggestions(pending.ruleSuggestions);
|
|
258
|
+
// #280 件1(0.30.4):durable 行腿的探针因由载体 = `riskDescriptor.probeCause`(server 对
|
|
259
|
+
// riskDescriptor 整体透传,与活卡帧同值)。SDK 6.17.2 的 riskDescriptor 声明尚无此键 ⇒ 结构
|
|
260
|
+
// 视图读(与 caps 防御读同款姿势),类型半场候 SDK 班车;载体形不合 ⇒ 不铸键(内部结构不校,
|
|
261
|
+
// 归呈卡端窄读器 —— isProbeCauseCarrier 顶注)。
|
|
262
|
+
const durableProbeCause = pending.riskDescriptor?.probeCause;
|
|
258
263
|
const card = await surfaceApprovalCard({
|
|
259
264
|
toolName,
|
|
260
265
|
args,
|
|
@@ -262,6 +267,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
262
267
|
...(signal ? { signal } : {}),
|
|
263
268
|
...(pending.governanceForced === true ? { governanceForced: true } : {}),
|
|
264
269
|
...(ruleSuggestionsReadOnly !== undefined ? { ruleSuggestionsReadOnly } : {}),
|
|
270
|
+
...(isProbeCauseCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
|
|
265
271
|
});
|
|
266
272
|
switch (card.kind) {
|
|
267
273
|
case 'failed':
|
|
@@ -347,6 +353,11 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
|
|
|
347
353
|
// (sdk 6.14.0/6.15.0 的 TOOL_APPROVAL_FRAME_KEYS 都还没有它)⇒ 对账门里有一条**带退出条件**的
|
|
348
354
|
// 领先登记(AHEAD_OF_ANCHOR):SDK 锚一旦补上,那条登记当场红,逼人删登记而不是让豁免长住。
|
|
349
355
|
'persistedRuleShadowed',
|
|
356
|
+
// #280 件1(client-core 0.30.4):server 7.21.0-rc(#253)起真发 `probeCause`(直证 = engine
|
|
357
|
+
// 7.22.0 fixture 的 `@sema-agent/server/dist/tool-approval.d.ts`)。⚠️ 本键**领先** SDK 运行期锚
|
|
358
|
+
// 一代(sdk 6.17.2 尚无)⇒ 对账门里有一条带退出条件的领先登记(AHEAD_OF_ANCHOR),SDK 锚一旦
|
|
359
|
+
// 补上,那条登记当场红,逼人删登记回到逐元素相等 —— #144 persistedRuleShadowed 同形先例。
|
|
360
|
+
'probeCause',
|
|
350
361
|
'delegation',
|
|
351
362
|
'outcome',
|
|
352
363
|
];
|
|
@@ -458,6 +469,16 @@ function pathFromGateMessage(message) {
|
|
|
458
469
|
* 渲一个按下去必被 server 拒的选项。上限 8 条(server 端候选本就 ≤2,超长=坏形,截不留痕会
|
|
459
470
|
* 掩盖注入,整体降缺席)。
|
|
460
471
|
*/
|
|
472
|
+
/**
|
|
473
|
+
* `probeCause` 的**载体形**判(#280 件1):非 null 非数组对象才搬运(wire 契约的载体就是对象,
|
|
474
|
+
* 串/数/数组/null 是坏载体不是「另一种因由」)。刻意只判到这一层 —— 内部结构(code/roots/further)
|
|
475
|
+
* 校验归呈卡端窄读器(server `readProbeCause` 的消费端对偶,壳 `probeCauseNote.readProbeCauseKey`),
|
|
476
|
+
* 包内校严一档就会把上游 additive 内键挡在边界外,还让「谁把键窄没的」在排障时多一层嫌疑。
|
|
477
|
+
* 两条决断腿(活帧/durable 行)共用这一把,判词绝不各写各的。
|
|
478
|
+
*/
|
|
479
|
+
function isProbeCauseCarrier(v) {
|
|
480
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
481
|
+
}
|
|
461
482
|
function readRuleSuggestions(v) {
|
|
462
483
|
if (!Array.isArray(v))
|
|
463
484
|
return undefined;
|
|
@@ -570,6 +591,9 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
570
591
|
...(typeof frame.persistedRuleShadowed === 'string' && frame.persistedRuleShadowed.trim() !== ''
|
|
571
592
|
? { persistedRuleShadowed: frame.persistedRuleShadowed }
|
|
572
593
|
: {}),
|
|
594
|
+
// #280 件1(0.30.4):探针因由随卡透传 —— 载体形判之外零校验零改写(内部结构归呈卡端窄读器,
|
|
595
|
+
// isProbeCauseCarrier 顶注);缺席/坏载体 ⇒ 键不 stamp,卡形字节不变。
|
|
596
|
+
...(isProbeCauseCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
|
|
573
597
|
});
|
|
574
598
|
const decision = card.kind === 'allow' ? (card.allowSession ? 'allow_session' : 'allow') : 'deny';
|
|
575
599
|
if (card.kind === 'failed') {
|
package/dist/index.d.ts
CHANGED
|
@@ -242,6 +242,10 @@ export * from './agentSession/backgroundView.js';
|
|
|
242
242
|
export * from './model/catalog.js';
|
|
243
243
|
export * from './model/catalogLoader.js';
|
|
244
244
|
export * from './model/providerAuth.js';
|
|
245
|
+
export * from './model/providerCatalog.js';
|
|
246
|
+
export * from './model/tierVocabulary.js';
|
|
245
247
|
export * from './websearch/searchProviderPresets.js';
|
|
246
248
|
export * from './env/localeGeo.js';
|
|
249
|
+
export * from './env/localeTag.js';
|
|
250
|
+
export * from './env/uiLanguage.js';
|
|
247
251
|
export type { PendingCheckpoint, RuleSuggestion } from '@sema-agent/sdk';
|
package/dist/index.js
CHANGED
|
@@ -416,6 +416,14 @@ export * from './model/catalogLoader.js';
|
|
|
416
416
|
// 填别的编辑器插件的 app id = 冒用,与 §6 判 C 档出局同一条判据),因此
|
|
417
417
|
// `supportsDeviceCodeAuth` 对每一家如实返回 false、UI 不显示该选项 —— 注册一家 = 加一行。
|
|
418
418
|
export * from './model/providerAuth.js';
|
|
419
|
+
// ── F4 族D(A-028.17/.18,2026-08-15):配置/模型表单的**折表与词表**单源 ─────────────────────
|
|
420
|
+
// 🔴 `model/providerCatalog.ts` = 46 家表的规范折表层(表→目录行的那一次投影 + 行级文案判定)。
|
|
421
|
+
// 此前 cli/web 各折各的(共三份),两侧常驻全表门靠人工对齐。三条约束成文在文件头:
|
|
422
|
+
// 单一真源现算 / 全表不重排不过滤 / 诚实缺席(零模型家说 "model id typed in" 不编 "0 models")。
|
|
423
|
+
export * from './model/providerCatalog.js';
|
|
424
|
+
// 🔴 `model/tierVocabulary.ts` = 档位五档词表 + CC 别名 + isTier 校验 + resolveTierBinding
|
|
425
|
+
// fail-open 降档派生。此前跨两仓三份;settings 读写那半场仍归宿主(cli tierCore 只留存储)。
|
|
426
|
+
export * from './model/tierVocabulary.js';
|
|
419
427
|
// ── 搜索 provider 目录 + 地域预选(2026-07-31)──────────────────────────────────────────────────
|
|
420
428
|
// `websearch/searchProviderPresets` = model 目录的**同形不同表**姊妹件:数据在 json、类型与查询
|
|
421
429
|
// 在 ts。它编译出来的不是模型目录而是**引擎部署 env**(`WEB_SEARCH_*`)。
|
|
@@ -430,3 +438,10 @@ export * from './websearch/searchProviderPresets.js';
|
|
|
430
438
|
// `src/model/**` 也不许 import 它(pure 门 GEO 段有机械断言)。合法用途只有搜索 provider /
|
|
431
439
|
// 镜像源地址这类「取哪个地址更可达」的**默认值**预选。
|
|
432
440
|
export * from './env/localeGeo.js';
|
|
441
|
+
// ── F4 族D(A-028.20,2026-08-15):locale tag 手术单源 + UI 语言词表/判定 ────────────────────
|
|
442
|
+
// 🔴 双出口成文(localeTag.ts 文件头):tag **手术**(剥 codeset/modifier、切子标签)单源一份;
|
|
443
|
+
// **判定**两个且输出域语义不同,刻意不收敛 —— `pickUiLanguage` 答「界面用哪种文字」
|
|
444
|
+
// (en/zh,未知回落可读的 en),`resolveRegionHint` 答「哪个地址更可达」(cn/intl/unknown,
|
|
445
|
+
// 未知绝不猜)。`zh-Hant` 在前者是 zh、在后者是 intl —— 这是设计,不是漂移。
|
|
446
|
+
export * from './env/localeTag.js';
|
|
447
|
+
export * from './env/uiLanguage.js';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* providerCatalog.ts — F4 族D(A-028.17,2026-08-15):46 家 provider 表的**规范折表层**
|
|
3
|
+
* (canonical fold)。纯派生、零 IO、零宿主依赖 —— 表本体在 `providerPresets.json`,
|
|
4
|
+
* 本件只做「表 → 目录行」的那一次投影与行级文案判定。
|
|
5
|
+
*
|
|
6
|
+
* ## 为什么折表要进包
|
|
7
|
+
*
|
|
8
|
+
* 搬迁前这张表被折了三次:cli `src/sema/config/providerDirectory.ts`(Model Hub 目录行)、
|
|
9
|
+
* web `src/lib/providers/catalog.ts`(向导折表)、web `provider-catalog.ts`(降级表二次映射)。
|
|
10
|
+
* 三份折表各自持有「host 怎么取 / 零模型家怎么说 / 顺序动不动」这些**判定**,两侧常驻全表门
|
|
11
|
+
* 只能靠人工对齐 —— 判定漂了没有任何编译器会响。折表进包后:cli 换装 re-export(单一真源),
|
|
12
|
+
* web 侧第二份折表退役是 web 半场的事(发布帖点名)。
|
|
13
|
+
*
|
|
14
|
+
* ## 三条继承自 cli #175 的设计约束(逐字保留语义)
|
|
15
|
+
*
|
|
16
|
+
* ① **单一真源**:每个量都从 `PROVIDER_PRESETS` 现算,本文件里没有第二张表 —— 没有硬编码的
|
|
17
|
+
* 家名/baseUrl/模型清单。表更新一次,所有端的目录跟着变。
|
|
18
|
+
* ② **全表不重排**:顺序 = 表序,一行不筛、不重排、不分组(clay 令 2026-08-01:模型 provider
|
|
19
|
+
* 完全用户选择,走全表)。本件不导出任何排序/过滤函数。
|
|
20
|
+
* ③ **诚实缺席**:表里没登记模型列表的家(ollama / vllm / litellm / 聚合网关等)`modelIds`
|
|
21
|
+
* 就是空数组,行文案说「model id typed in」,**不写「0 models」**——「0 个模型」是编造
|
|
22
|
+
* 出来的量:表没登记 ≠ 这家没有模型。consoleUrl / signupUrl 缺席同样原样缺席,不回落编造。
|
|
23
|
+
*
|
|
24
|
+
* 行形是 cli DirectoryRow 与 web 折表两侧字段的**严格超集**(字段名与 cli 侧逐字一致,cli 直接
|
|
25
|
+
* re-export 零改动;web 侧的 label/keyEnvSuggestion/suggestedModels 对应 name/authEnv/modelIds)。
|
|
26
|
+
*/
|
|
27
|
+
import { type ProviderPreset } from './providerPresets.js';
|
|
28
|
+
/** 目录/向导的一行(表的一次纯投影;字段全部现算,invariant:modelCount === modelIds.length)。 */
|
|
29
|
+
export interface ProviderCatalogRow {
|
|
30
|
+
/** preset 表里的 id(行 key + 回查用) */
|
|
31
|
+
presetId: string;
|
|
32
|
+
name: string;
|
|
33
|
+
baseUrl: string;
|
|
34
|
+
/** baseUrl 的 host(含端口);解析不出时给空串(诚实缺席,不猜) */
|
|
35
|
+
host: string;
|
|
36
|
+
api: ProviderPreset['api'];
|
|
37
|
+
/** 领 key 的环境变量**名**(绝不是值;web 侧的 keyEnvSuggestion) */
|
|
38
|
+
authEnv: string;
|
|
39
|
+
/** 表里登记了的模型 id(原序原样;空数组 = 这家的 model id 由用户手填,见约束③) */
|
|
40
|
+
modelIds: string[];
|
|
41
|
+
/** 表里**登记了**的模型条数(0 = model id 手填,不是「这家没有模型」) */
|
|
42
|
+
modelCount: number;
|
|
43
|
+
/** 聚合网关/本地引擎:模型 id 手填,ctx/maxTokens 走家族表 */
|
|
44
|
+
modelViaFamily: boolean;
|
|
45
|
+
/** 去哪拿 key 的控制台页(纯 URL);缺席 = 上游没有确证地址,不编 */
|
|
46
|
+
consoleUrl?: string;
|
|
47
|
+
/** 没账号时的注册入口;缺席 = 用 consoleUrl 那条 */
|
|
48
|
+
signupUrl?: string;
|
|
49
|
+
}
|
|
50
|
+
/** api 家族的行内短名(目录行/芯片行两端同一个词,不各自缩写)。 */
|
|
51
|
+
export declare const providerApiShortName: (api: ProviderPreset["api"]) => string;
|
|
52
|
+
/**
|
|
53
|
+
* 全表 → 目录行。**顺序 = 表序**(约束②):这里没有 filter,也没有 sort。
|
|
54
|
+
* `presets` 入参只给测试用(注入一张小表),产品路径恒走 `PROVIDER_PRESETS`。
|
|
55
|
+
*/
|
|
56
|
+
export declare function providerCatalogRows(presets?: readonly ProviderPreset[]): ProviderCatalogRow[];
|
|
57
|
+
/** 目录规模。端上「家数」与目录行数同源 —— 两处不会各报一个数。 */
|
|
58
|
+
export declare function providerCatalogCount(): number;
|
|
59
|
+
/** 按 preset id 回查整条 preset(目录选中之后要拿 baseUrl / models / consoleUrl)。 */
|
|
60
|
+
export declare function providerPresetById(presetId: string): ProviderPreset | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* 一行的 dim 说明:`host · api · 模型情况`。
|
|
63
|
+
* 约束③:`modelCount === 0` 说「model id typed in」,绝不写「0 models」。
|
|
64
|
+
*/
|
|
65
|
+
export declare function providerCatalogRowDetail(row: Pick<ProviderCatalogRow, 'host' | 'baseUrl' | 'api' | 'modelCount' | 'modelViaFamily'>): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* providerCatalog.ts — F4 族D(A-028.17,2026-08-15):46 家 provider 表的**规范折表层**
|
|
3
|
+
* (canonical fold)。纯派生、零 IO、零宿主依赖 —— 表本体在 `providerPresets.json`,
|
|
4
|
+
* 本件只做「表 → 目录行」的那一次投影与行级文案判定。
|
|
5
|
+
*
|
|
6
|
+
* ## 为什么折表要进包
|
|
7
|
+
*
|
|
8
|
+
* 搬迁前这张表被折了三次:cli `src/sema/config/providerDirectory.ts`(Model Hub 目录行)、
|
|
9
|
+
* web `src/lib/providers/catalog.ts`(向导折表)、web `provider-catalog.ts`(降级表二次映射)。
|
|
10
|
+
* 三份折表各自持有「host 怎么取 / 零模型家怎么说 / 顺序动不动」这些**判定**,两侧常驻全表门
|
|
11
|
+
* 只能靠人工对齐 —— 判定漂了没有任何编译器会响。折表进包后:cli 换装 re-export(单一真源),
|
|
12
|
+
* web 侧第二份折表退役是 web 半场的事(发布帖点名)。
|
|
13
|
+
*
|
|
14
|
+
* ## 三条继承自 cli #175 的设计约束(逐字保留语义)
|
|
15
|
+
*
|
|
16
|
+
* ① **单一真源**:每个量都从 `PROVIDER_PRESETS` 现算,本文件里没有第二张表 —— 没有硬编码的
|
|
17
|
+
* 家名/baseUrl/模型清单。表更新一次,所有端的目录跟着变。
|
|
18
|
+
* ② **全表不重排**:顺序 = 表序,一行不筛、不重排、不分组(clay 令 2026-08-01:模型 provider
|
|
19
|
+
* 完全用户选择,走全表)。本件不导出任何排序/过滤函数。
|
|
20
|
+
* ③ **诚实缺席**:表里没登记模型列表的家(ollama / vllm / litellm / 聚合网关等)`modelIds`
|
|
21
|
+
* 就是空数组,行文案说「model id typed in」,**不写「0 models」**——「0 个模型」是编造
|
|
22
|
+
* 出来的量:表没登记 ≠ 这家没有模型。consoleUrl / signupUrl 缺席同样原样缺席,不回落编造。
|
|
23
|
+
*
|
|
24
|
+
* 行形是 cli DirectoryRow 与 web 折表两侧字段的**严格超集**(字段名与 cli 侧逐字一致,cli 直接
|
|
25
|
+
* re-export 零改动;web 侧的 label/keyEnvSuggestion/suggestedModels 对应 name/authEnv/modelIds)。
|
|
26
|
+
*/
|
|
27
|
+
import { PROVIDER_PRESETS } from './providerPresets.js';
|
|
28
|
+
const hostOf = (u) => {
|
|
29
|
+
try {
|
|
30
|
+
return new URL(u).host;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/** api 家族的行内短名(目录行/芯片行两端同一个词,不各自缩写)。 */
|
|
37
|
+
export const providerApiShortName = (api) => api === 'anthropic-messages' ? 'anthropic' : 'openai';
|
|
38
|
+
/**
|
|
39
|
+
* 全表 → 目录行。**顺序 = 表序**(约束②):这里没有 filter,也没有 sort。
|
|
40
|
+
* `presets` 入参只给测试用(注入一张小表),产品路径恒走 `PROVIDER_PRESETS`。
|
|
41
|
+
*/
|
|
42
|
+
export function providerCatalogRows(presets = PROVIDER_PRESETS) {
|
|
43
|
+
return presets.map(p => ({
|
|
44
|
+
presetId: p.id,
|
|
45
|
+
name: p.name,
|
|
46
|
+
baseUrl: p.baseUrl,
|
|
47
|
+
host: hostOf(p.baseUrl),
|
|
48
|
+
api: p.api,
|
|
49
|
+
authEnv: p.authEnv,
|
|
50
|
+
modelIds: p.models.map(m => m.id),
|
|
51
|
+
modelCount: p.models.length,
|
|
52
|
+
modelViaFamily: p.modelViaFamily === true,
|
|
53
|
+
...(p.consoleUrl === undefined ? {} : { consoleUrl: p.consoleUrl }),
|
|
54
|
+
...(p.signupUrl === undefined ? {} : { signupUrl: p.signupUrl }),
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
/** 目录规模。端上「家数」与目录行数同源 —— 两处不会各报一个数。 */
|
|
58
|
+
export function providerCatalogCount() {
|
|
59
|
+
return providerCatalogRows().length;
|
|
60
|
+
}
|
|
61
|
+
/** 按 preset id 回查整条 preset(目录选中之后要拿 baseUrl / models / consoleUrl)。 */
|
|
62
|
+
export function providerPresetById(presetId) {
|
|
63
|
+
return PROVIDER_PRESETS.find(p => p.id === presetId);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 一行的 dim 说明:`host · api · 模型情况`。
|
|
67
|
+
* 约束③:`modelCount === 0` 说「model id typed in」,绝不写「0 models」。
|
|
68
|
+
*/
|
|
69
|
+
export function providerCatalogRowDetail(row) {
|
|
70
|
+
const models = row.modelCount === 0
|
|
71
|
+
? 'model id typed in'
|
|
72
|
+
: `${row.modelCount} model${row.modelCount === 1 ? '' : 's'}${row.modelViaFamily ? ' · or any id' : ''}`;
|
|
73
|
+
return [row.host || row.baseUrl, providerApiShortName(row.api), models].join(' · ');
|
|
74
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tierVocabulary.ts — F4 族D(A-028.18,2026-08-15):档位(tier)的**词表 + 派生 + 校验**单源。
|
|
3
|
+
* 纯数据 + 纯函数,零 IO、零宿主依赖。
|
|
4
|
+
*
|
|
5
|
+
* ## 为什么进包
|
|
6
|
+
*
|
|
7
|
+
* 搬迁前这套词表跨两仓共三份:cli `src/sema/config/tierCore.ts`(词表 + isTier 校验 +
|
|
8
|
+
* resolveTierBinding 派生)、web `tiers-page.tsx`(词表)、web `chat/wording.ts`(词表 + 按
|
|
9
|
+
* 模型名 token 化的派生)。五档顺序/别名映射漂了没有任何编译器会响。词表进包后:cli 的
|
|
10
|
+
* tierCore 换装 re-export(settings 读写那半场留在壳里 —— 那是宿主存储,不是词表);web 侧
|
|
11
|
+
* 两份的退役是 web 半场的事(发布帖点名)。
|
|
12
|
+
*
|
|
13
|
+
* ## 与引擎侧的对齐
|
|
14
|
+
*
|
|
15
|
+
* 五档序镜像 core `DEFAULT_TIER_ORDER`、CC 别名镜像 core `CC_MODEL_TIER_ALIASES`(cli 侧
|
|
16
|
+
* tierStore.test.ts 有对 core 导出的运行时 drift-lock);`ultra` 预留在 max 之上(core 已排位,
|
|
17
|
+
* UI 今天不面)。缺省默认档 pro(clay [599])。
|
|
18
|
+
*/
|
|
19
|
+
/** sema 档位(用户/模型可寻址的命名档)。`ultra` 预留(core 已排位,UI 今天不面)。 */
|
|
20
|
+
export type Tier = 'ultra' | 'max' | 'pro' | 'flash' | 'lite';
|
|
21
|
+
/** 降档链(强→弱)——镜像 core DEFAULT_TIER_ORDER;解析某档无绑定时沿链向后取第一个有绑定的档。 */
|
|
22
|
+
export declare const TIER_ORDER: readonly Tier[];
|
|
23
|
+
/** UI 呈现的四档(ultra 预留不面)。 */
|
|
24
|
+
export declare const SURFACED_TIERS: readonly Tier[];
|
|
25
|
+
/** CC 档位词 → sema 档位(内置固定映射,镜像 core CC_MODEL_TIER_ALIASES;兼容层不可配)。 */
|
|
26
|
+
export declare const CC_TIER_ALIASES: Readonly<Record<string, Tier>>;
|
|
27
|
+
/** 缺省默认档(clay [599]:默认档缺省 pro)。 */
|
|
28
|
+
export declare const DEFAULT_TIER: Tier;
|
|
29
|
+
/** 各档一句话职责(档位 Hub/onboard 行描述共用;无 roster/role 词)。 */
|
|
30
|
+
export declare const TIER_BLURBS: Readonly<Record<Tier, string>>;
|
|
31
|
+
/** 一个命名档位组的绑定形(纯类型:绑定值 = 端侧模型目录的 entry id)。 */
|
|
32
|
+
export type TierGroupDef = {
|
|
33
|
+
/** tier → 模型 entry id。未绑档沿 TIER_ORDER 向下 fail-open(见 resolveTierBinding)。 */
|
|
34
|
+
tiers: Partial<Record<Tier, string>>;
|
|
35
|
+
/** 组内默认档(缺省 DEFAULT_TIER=pro)。 */
|
|
36
|
+
defaultTier?: Tier;
|
|
37
|
+
};
|
|
38
|
+
export declare const isTier: (v: unknown) => v is Tier;
|
|
39
|
+
/**
|
|
40
|
+
* 组内解析某档 → 绑定 entry id(该档未绑则沿 TIER_ORDER 向下取第一个有绑定的档;全空
|
|
41
|
+
* undefined)。fail-open 沿链向下,镜像 core expandTiers 语义。
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveTierBinding(def: TierGroupDef, tier: Tier): string | undefined;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tierVocabulary.ts — F4 族D(A-028.18,2026-08-15):档位(tier)的**词表 + 派生 + 校验**单源。
|
|
3
|
+
* 纯数据 + 纯函数,零 IO、零宿主依赖。
|
|
4
|
+
*
|
|
5
|
+
* ## 为什么进包
|
|
6
|
+
*
|
|
7
|
+
* 搬迁前这套词表跨两仓共三份:cli `src/sema/config/tierCore.ts`(词表 + isTier 校验 +
|
|
8
|
+
* resolveTierBinding 派生)、web `tiers-page.tsx`(词表)、web `chat/wording.ts`(词表 + 按
|
|
9
|
+
* 模型名 token 化的派生)。五档顺序/别名映射漂了没有任何编译器会响。词表进包后:cli 的
|
|
10
|
+
* tierCore 换装 re-export(settings 读写那半场留在壳里 —— 那是宿主存储,不是词表);web 侧
|
|
11
|
+
* 两份的退役是 web 半场的事(发布帖点名)。
|
|
12
|
+
*
|
|
13
|
+
* ## 与引擎侧的对齐
|
|
14
|
+
*
|
|
15
|
+
* 五档序镜像 core `DEFAULT_TIER_ORDER`、CC 别名镜像 core `CC_MODEL_TIER_ALIASES`(cli 侧
|
|
16
|
+
* tierStore.test.ts 有对 core 导出的运行时 drift-lock);`ultra` 预留在 max 之上(core 已排位,
|
|
17
|
+
* UI 今天不面)。缺省默认档 pro(clay [599])。
|
|
18
|
+
*/
|
|
19
|
+
/** 降档链(强→弱)——镜像 core DEFAULT_TIER_ORDER;解析某档无绑定时沿链向后取第一个有绑定的档。 */
|
|
20
|
+
export const TIER_ORDER = ['ultra', 'max', 'pro', 'flash', 'lite'];
|
|
21
|
+
/** UI 呈现的四档(ultra 预留不面)。 */
|
|
22
|
+
export const SURFACED_TIERS = ['max', 'pro', 'flash', 'lite'];
|
|
23
|
+
/** CC 档位词 → sema 档位(内置固定映射,镜像 core CC_MODEL_TIER_ALIASES;兼容层不可配)。 */
|
|
24
|
+
export const CC_TIER_ALIASES = {
|
|
25
|
+
fable: 'max',
|
|
26
|
+
mythos: 'max',
|
|
27
|
+
opus: 'pro',
|
|
28
|
+
sonnet: 'flash',
|
|
29
|
+
haiku: 'lite',
|
|
30
|
+
};
|
|
31
|
+
/** 缺省默认档(clay [599]:默认档缺省 pro)。 */
|
|
32
|
+
export const DEFAULT_TIER = 'pro';
|
|
33
|
+
/** 各档一句话职责(档位 Hub/onboard 行描述共用;无 roster/role 词)。 */
|
|
34
|
+
export const TIER_BLURBS = {
|
|
35
|
+
ultra: 'reserved above max — binds when a stronger model lands',
|
|
36
|
+
max: 'hardest problems — deep reasoning, big refactors',
|
|
37
|
+
pro: 'everyday driving — the default tier',
|
|
38
|
+
flash: 'fast + cheap — titles, recaps, volume work',
|
|
39
|
+
lite: 'cheapest — bulk classification, simple lookups',
|
|
40
|
+
};
|
|
41
|
+
export const isTier = (v) => typeof v === 'string' && TIER_ORDER.includes(v);
|
|
42
|
+
/**
|
|
43
|
+
* 组内解析某档 → 绑定 entry id(该档未绑则沿 TIER_ORDER 向下取第一个有绑定的档;全空
|
|
44
|
+
* undefined)。fail-open 沿链向下,镜像 core expandTiers 语义。
|
|
45
|
+
*/
|
|
46
|
+
export function resolveTierBinding(def, tier) {
|
|
47
|
+
const direct = def.tiers[tier];
|
|
48
|
+
if (direct)
|
|
49
|
+
return direct;
|
|
50
|
+
const at = TIER_ORDER.indexOf(tier);
|
|
51
|
+
if (at === -1)
|
|
52
|
+
return undefined;
|
|
53
|
+
for (let i = at + 1; i < TIER_ORDER.length; i++) {
|
|
54
|
+
const next = def.tiers[TIER_ORDER[i]];
|
|
55
|
+
if (next)
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
@@ -20,6 +20,26 @@ export interface SubagentActivity {
|
|
|
20
20
|
export type SubagentActivitySink = (a: SubagentActivity) => void;
|
|
21
21
|
/** 装/卸 Progress 段落点。传 null 卸。返回还原函数。 */
|
|
22
22
|
export declare function installSubagentActivitySink(sink: SubagentActivitySink | null): () => void;
|
|
23
|
+
/**
|
|
24
|
+
* 一帧 tail `event: meta` 的发布载荷(#280 件2,0.30.4)。`meta` 是连接语义帧(server 建连即发:
|
|
25
|
+
* `{version, runId, target, status, seq?, contentFrames?, …}`),**不进内容面** —— 但其中的
|
|
26
|
+
* **供给形判别位 `contentFrames`**(server e439a6c,闭集 {on | progress_only | unknown})是端
|
|
27
|
+
* 区分「内容帧**永远不会来**(forward 缺席宿主,结构性无内容)vs **还没来**」的唯一诚实依据;
|
|
28
|
+
* 0.30.3 及之前它被消费循环的 forward-only 过滤整类 skip,端结构性到不了。
|
|
29
|
+
* 🔴 `meta` 为**开集原文**(SDK subagentStream 产物 `Record<string, unknown>` 原样):判别位的
|
|
30
|
+
* 窄读(闭集判词/缺席处置)归消费端,包只搬运 —— additive 上游往 meta 里加键零施工。
|
|
31
|
+
* ⚠️ 供给形登记随句柄终态在 server 侧撤销(SubagentTailBus.forgetHandleContentMode)⇒ 判别位
|
|
32
|
+
* 要在子代存活窗内消费,终态后重开的 tail 读到 `unknown` 不是回归。
|
|
33
|
+
*/
|
|
34
|
+
export interface SubagentTailMeta {
|
|
35
|
+
taskId: string;
|
|
36
|
+
/** meta 帧原文(开集;`contentFrames` 等判别位的窄读归消费端)。 */
|
|
37
|
+
meta: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** 端装的 meta 帧落点(#280 件2)。不装 ⇒ 判别位不达端(不是错误态,该面不启用;0.30.3 现状字节不变)。 */
|
|
40
|
+
export type SubagentTailMetaSink = (m: SubagentTailMeta) => void;
|
|
41
|
+
/** 装/卸 tail meta 帧落点。传 null 卸。返回还原函数。 */
|
|
42
|
+
export declare function installSubagentTailMetaSink(sink: SubagentTailMetaSink | null): () => void;
|
|
23
43
|
/**
|
|
24
44
|
* tail 是否正对该行开着 —— **两源互斥判别口**(行帧 `currentTool` 臂在 tail 活跃时让位:
|
|
25
45
|
* tail 的 tool_start 带真 args,渲染更细;两源同录 = 每步双行)。
|
|
@@ -84,6 +84,15 @@ export function installSubagentActivitySink(sink) {
|
|
|
84
84
|
activitySink = prev;
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
+
let tailMetaSink = null;
|
|
88
|
+
/** 装/卸 tail meta 帧落点。传 null 卸。返回还原函数。 */
|
|
89
|
+
export function installSubagentTailMetaSink(sink) {
|
|
90
|
+
const prev = tailMetaSink;
|
|
91
|
+
tailMetaSink = sink;
|
|
92
|
+
return () => {
|
|
93
|
+
tailMetaSink = prev;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
87
96
|
/* `coerceOutput`(tool_end.output 的字符串化口)**已搬到 `subagentContentStore.ts`**
|
|
88
97
|
* (#158 移交① / [3674](d) 姊妹病,2026-08-12):喂那个 store 的是两条腿(本 tail 腿 + seam
|
|
89
98
|
* 适配器的 C1 分流臂),字符串化口必须只有一份,故跟着它服务的那个位走。公面导出名不变
|
|
@@ -143,8 +152,35 @@ export function tailEngineSubagent(taskId) {
|
|
|
143
152
|
// 连接分界线:meta 帧即到 = SSE 建连成功(与「子代此刻恰好在跑长工具、没内容可发」区分开)
|
|
144
153
|
hostLog('debug', `[subagent-tail] ${taskId} stream connected +${Date.now() - openedAt}ms`);
|
|
145
154
|
}
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
// #280 件2(0.30.4):帧分类闸 —— `meta` 经发布口出去(`contentFrames` 判别位在其中,
|
|
156
|
+
// 见 SubagentTailMeta 顶注),`forward` 进内容面,其余(heartbeat / 未来新连接语义帧)
|
|
157
|
+
// 维持 skip。🔴 开闸范围**只放 meta**(最小化):放宽 default 臂 = 把未知帧灌进发布口,
|
|
158
|
+
// 消费端会把连接杂音当判别位读。
|
|
159
|
+
switch (ev.event) {
|
|
160
|
+
case 'meta':
|
|
161
|
+
// 连接语义帧,不进内容面;不装 sink = 现状。🔴 消费端 sink 失败**绝不撕裂 tail**
|
|
162
|
+
//(本文件头注铁律「一切失败 fail-soft…绝不 throw」;runStream onDroppedFrame
|
|
163
|
+
// 「sink 抛错绝不打断流且痕迹落回」同族)——两臂都要:同步 throw 由 try/catch 兜,
|
|
164
|
+
// **异步拒绝**(类型虽 void,宿主传 async 函数合法)也要有界观察 —— 不观察 =
|
|
165
|
+
// unhandledRejection = Node 缺省整进程崩,TUI 宿主最坏形。痕迹留 hostLog,内容帧照常消费。
|
|
166
|
+
try {
|
|
167
|
+
const out = tailMetaSink?.({ taskId, meta: ev.data });
|
|
168
|
+
if (out !== null && typeof out === 'object' && typeof out.then === 'function') {
|
|
169
|
+
;
|
|
170
|
+
out.then(undefined, (e) => {
|
|
171
|
+
hostLog('debug', `[subagent-tail] ${taskId} meta sink rejected: ${String(e).slice(0, 160)}`);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
hostLog('debug', `[subagent-tail] ${taskId} meta sink threw: ${String(e).slice(0, 160)}`);
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
case 'forward':
|
|
180
|
+
break;
|
|
181
|
+
default:
|
|
182
|
+
continue; // heartbeat 等连接语义帧,不进内容面
|
|
183
|
+
}
|
|
148
184
|
const d = ev.data;
|
|
149
185
|
const t = d.type;
|
|
150
186
|
frames += 1;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=6.17.2**(value-level,非 type-only) | `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.2.0**(type-only,零运行时) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
26
|
-
| 公开导出面 | **
|
|
26
|
+
| 公开导出面 | **724** 个运行期符号(+ 33 个测试钩) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
|
|
27
27
|
| 常驻门 | 以 `scripts/gates-manifest.json` 的 `suites` 长度为准(**本档不抄这个数**) | `scripts/gates-manifest.json`;`npm test` 的名单等值门与它逐名对账 |
|
|
28
28
|
| 沿革档 | 0.29.0 起建 `CHANGELOG.md`;更早批次记账在 `src/index.ts` 文件头 + `docs/REFACTOR-LEDGER.md` | — |
|
|
29
29
|
|
|
@@ -99,7 +99,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
99
99
|
|
|
100
100
|
## §2 公共导出面地图(按域)
|
|
101
101
|
|
|
102
|
-
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**
|
|
102
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**724** 项)。
|
|
103
103
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
104
104
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
105
105
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -109,38 +109,38 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
109
109
|
|
|
110
110
|
`public-export-baseline.json` 由 **`dist/index.js` 的运行期导出**生成(生成口径自述见
|
|
111
111
|
`scripts/run-client-core-typeshape-test.mjs`,双向精确集合门在 `scripts/run-public-surface-test.mjs`)。
|
|
112
|
-
实测:
|
|
112
|
+
实测:724 项 **100% 是运行期导出,零 type-only**。
|
|
113
113
|
|
|
114
114
|
**推论(端必须知道)**:
|
|
115
|
-
- barrel 导出的**类型**面比
|
|
115
|
+
- barrel 导出的**类型**面比 707 大得多,且**不被这道门看守** —— `AdapterContext` / `SeamEvent` /
|
|
116
116
|
`ChromeEvent` / `HostPorts` / `ApprovalCardPort` / `HitlHostSurface` / `ClientSliceLike` /
|
|
117
117
|
`LocalSessionEvent` / `SeatMethodName` / `ModelCatalog` 全在公面上、全**不在**基线里。
|
|
118
118
|
端依赖这些类型是合法的,但**不要**拿基线 diff 当"类型面没变"的证据。
|
|
119
119
|
- `src/agentSession/contract.ts` 对基线贡献 **0** 项(纯类型模块,`export *` 在 dist 里是空转发)。
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
724 项的内部构成(帮助端估读表大小):**217** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
|
|
122
122
|
(矩阵、键集、env 名、锚串)而非可调用物;**4** 项是 PascalCase 运行期值
|
|
123
123
|
(`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError`);
|
|
124
124
|
**39** 项是 `*For(sessionKey, …)` 的 per-session 变体(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
|
|
125
125
|
|
|
126
|
-
### 2b. 域图(16 域,逐域计数之和 =
|
|
126
|
+
### 2b. 域图(16 域,逐域计数之和 = 724)
|
|
127
127
|
|
|
128
128
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
129
129
|
|---|---|---|---|---|---|
|
|
130
130
|
| 1 | **适配内核(下行主链)** | 29 | `adapt` · `createWireToCcAdapter` · `runStream` · `eventToSdkMessage` · `terminalToSdkResult` · `turnUsageToModelUsage` · `isRunStreamActive` · `ADAPTER_DIVERGENCES` | 引擎 SSE `AgentEvent` → 端要渲的**双面输出**:transcript(`SDKMessage`)+ chrome(瞬态 `ChromeEvent`)。**本包存在的理由** | `src/adapt.ts`、`src/adapt/{arms,wireShapes,panelTasks}.ts`(经 `adapt.ts` 再导出)、`src/adapter/runStream.ts`、`src/adapter/downstream/*`、`src/adapter/types.ts` |
|
|
131
131
|
| 2 | **seam 公共契约** | 2(其余为 type-only) | `CHROME_ARMS` · `deriveTranscriptId` | 公共词汇 + **id 确定性不变量**(同一条流重放 ⇒ 同一串 id)。`CHROME_ARMS` = 端「我要消费哪些 chrome 臂」的对照清单 | `src/seam.ts` |
|
|
132
132
|
| 3 | **HITL 决断卡链**(§4/§5 主战场) | 119 | `makeHitlCanUseTool` · `HitlBridge` · `findPendingForTask` · `HitlSafetyError` · `bridgeAskUserQuestionGates` · `surfaceToolApprovalFrameAndRespond` / `surfaceFsApprovalAndDecide` · `readToolApprovalRespondAck` · `installApprovalCardPort(For)` · `installHitlHostSurface(For)` · `armPlanReviewApproval` · `reopenPlanReviewCard` · `decidePlanReview` · `startApprovalsFeed` · `pendingRowIsOwnedByThisSession` · `approvalCallKey`/`liveFrameCallKey`/`planReviewQuestionId` · `registerArmedGateFor`/`wasGateArmedFor`/`clearArmedGateFor` · `waitForGateArmed(For)`/`onGateArmed(For)`/`gateArmedWaitMs`(#244 F1 呈现回执事件源) · `planReviewArmedKey(For)`/`notePlanReviewAnswered(For)`/`notePlanReviewAnsweredIfDecisive(For)`(A-024.4 plan 呈现分代) · `toolEndOutputText` · `isAskTool` · `waitForParkRowBirth` · `classifyAskParkRows` / `askParkRowArm` / `classifyAskParkChainFailure` · `readDecisionNoteAudit` / `decisionNoteAuditLine` · `resumeRunningOptions` / `resumeChoiceFromLabels` · `persistedRulesLaneAvailable`/`persistedRulesGovernanceAvailable` · `classifyRulesFailure` · `listAllPersistedRules` · `classifySkippedReason` · `readRulePersistOutcome`(#244 F2:persist-ack 读口与 `readToolApprovalRespondAck` 合成一处) · `parseLocalAllowRule`(durable 腿本地落规则窄化骨架,谓词经 `LocalAllowRuleDeps` 注入) | suspended→decide→resume 环。🔴 **D-1 两元组 verbatim 回显**是字节级断言的安全不变量,端**不许重实现它的任何一段**。🔴 键空间边界(web [C1] d3 拦截):`gateIdentity` 四常量两函数只覆盖 HITL questionId/callKey 空间;seat 的 `TOOL_PERMISSION_REQUEST_ID_DOMAINS`(`plan:` 等)是另一键空间,**两者绝不合并**(合并=座位校验器静默拒全部 plan-review 卡) | `src/hitl/hitlBridge.ts`、`toolApprovalWire.ts`、`askGateWire.ts`、`planReviewWire.ts`、`hitlHostSurface.ts`、`gateIdentity.ts`、`armedGateRegistry.ts`、`parkOwnership.ts`、`parkResolver.ts`、`approvalsFeed.ts`、`frameRouter.ts`(**只挑名导出** `toolEndOutputText`/`ENGINE_ABORT_TOOL_RESULT`/`isAskTool`/`HITL_REJECT_MESSAGE`)、`parkRowBirthWait.ts`、`approvalDecisionNoteAudit.ts`、`askParkRowRouting.ts`、`resumeRunningCard.ts`(#265 上收的判定层)、`persistedRulesWire.ts`、`localAllowRule.ts`(#244 F2 规则侧) |
|
|
133
|
-
| 4 | **子代 wire + 面板侧信道台账** |
|
|
133
|
+
| 4 | **子代 wire + 面板侧信道台账** | 69 | `tailEngineSubagent` · `installSubagentActivitySink` · `installSubagentTailMetaSink`(#280 件2:tail meta 帧发布口,`contentFrames` 判别位载体)· `stopEngineTask` + `classifyTaskStopConflict` · `fetchEngineSubagentReport` · `steerEngineSubagent` · `subscribeSubagentContent` · `subscribeEngineAgentPanel` · `publishQuestionFrame` / `respondToQuestion` | 驱动与观测委派子代;经 module 级台账喂活体 agent/task 面板。全部**能力位 gate**(§5b) | `src/subagent/*.ts`、`src/subagentContentStore.ts`、`src/engineAgentPanelStore.ts`、`src/engineInlineTaskStats.ts`、`src/engineToolLabelStore.ts`、`src/liveQuestionStore.ts` |
|
|
134
134
|
| 5 | **fleet 投影** | 43 | `createFleetLedger` · `projectTasks` · `projectWorkflows` · `projectFleetAgentRows` · `readEngineActiveBgTasks` · `FLEET_TASK_VIEW_KEYS` | 老 `fleetClient` 那一刀的成品:**帧体归库、连接归端** —— 端持 SSE 连接,库做行投影 + 保留台账 | `src/fleet/fleetProjection.ts`、`src/fleet/fleetLedger.ts`、`src/fleetAgentPanelProjection.ts`、`src/fleetTaskDesc.ts` |
|
|
135
135
|
| 6 | **请求装配(上行唯一构造口)** | 8 | `buildTaskRequest` · `REQUEST_FIELD_MATRIX` · `unregisteredRequestKeys` · `applyLiveRequestDefaults` · `taskNotificationToPrintFrame` | 两条车道(`interactive`/`print`)出站请求的**唯一**构造器;`unregisteredRequestKeys` 是可执行门 —— 端偷带一个未登记键上 wire 就红 | `src/request/taskRequest.ts`、`src/request/printNotification.ts` |
|
|
136
136
|
| 7 | **通知与 outstanding 台账** | 37 | `installNotificationQueuePort` · `normalizeTaskNotification` · `taskNotificationDedupKeyFromWire` · `registerOutstandingBgTask` / `registerOutstandingWorkflowRun` · `notificationQueuePortMisses` · `subscribeOutstandingWorkflows` · `outstandingDeliverableWorkflowCount` | `task_notification` 归一 + 去重 + 投递进宿主命令队列的**一把闸**;`outstandingDeliverableWorkflowCount()` 是 headless `-p` 的**退出门** | `src/notifications.ts`(11 个 module 台账) |
|
|
137
137
|
| 8 | **工具结果卡** | 25 | `structuredToToolUseResult` · `readAsyncLaunchedAgentReceipt` · `wireOutputToBody` · `parseModelFacingBash` · `getPatchFromContents` · `toolEndResultToUserFrame` · `flattenToolOutput` | 铸端要渲的 `tool_result` 卡体,含客户端 diff hunk(唯一 runtime dep 的用处) | `src/toolResult.ts`、`src/printToolResultFrame.ts`、`src/diff/patch.ts` |
|
|
138
138
|
| 9 | **能力/旋钮 wire 门族** | 86 | `projectAgentsForWire` / `awaitTaskAgentsWire` · `hooksForWire` · `mcpConfigsToSpecs` · `ultracodeForRequest` · `resolveWebSearch` + `buildWebSearchEnv` · `attachmentsForRequest` · `projectEffectiveBody` | 逐功能的「引擎有没有这个能力 + 这个字段怎么上 wire」投影器,由 `buildTaskRequest` 组装。🔴 `hooksForWire()` 是 **fail-closed**:无 `SettingsPort` ⇒ 返回 `undefined`(信任门,不是取值) | 17 个 `src/*WireCaps.ts` + `src/scratchpadWireCaps.ts`、`src/hooksWireCaps.ts`、`src/goalStopHook.ts`、`src/websearch/searchProviderPresets.ts` |
|
|
139
139
|
| 10 | **headless / 部署旋钮 wire** | 61 | `parseSandboxArgv` / `sandboxRequestFields` · `parseLimitsArgv` / `limitsForPrint` · `resolveHeadlessFinalVerify` · `resolveHeadlessPermissionMode` · `resolveHeadlessInteractiveTools` · `armDetachCancel` + `detachCancelArm` + `isDetachArmed` · `withHeadlessR1Reconnect` | `-p`/headless 车道的 env+argv 旋钮。🔴 `detachWire` 是**拆**的补偿:判定与 cancel-arm 台账在库,信号路径的裸 fetch 留宿主(`detachCancelArm()` 是取件口) | `src/sandboxWire.ts`、`scenarioWire.ts`、`finalVerifyWire.ts`、`limitsWire.ts`、`interactiveToolsWire.ts`、`headlessPermissionModeWire.ts`、`headlessReconnectWire.ts`、`detachWire.ts` |
|
|
140
|
-
| 11 | **模型目录与预算** |
|
|
140
|
+
| 11 | **模型目录与预算** | 64 | `resolveModelCatalog` · `loadCatalogWithSources` · `PROVIDER_PRESETS` / `MODEL_FAMILIES` · `defaultMaxTokensFor` · `getLiveModelCatalog` / `setLiveModelCatalogRefresher` · `providerAuthMethods` / `beginDeviceCodeAuth` · `providerCatalogRows` / `providerCatalogRowDetail` / `providerPresetById`(#244 F4 族D A-028.17:46 家表的规范折表层 —— 全表不重排、诚实缺席「model id typed in」,cli 目录/web 向导同一份折表)· `TIER_ORDER` / `CC_TIER_ALIASES` / `isTier` / `resolveTierBinding`(A-028.18:档位词表+校验+fail-open 降档派生单源;settings 存储归宿主) | 三层 provider 目录解析(线上 URL → 包内预设 → 用户覆盖)+ per-model `maxTokens` 封顶。线上腿需注入 `CatalogFetchJson`,缺席 ⇒ 整条不启用(`online.reason='no-fetch-port'`);缓存落盘经 `CatalogCachePort` | `src/model/{catalog,catalogLoader,providerAuth,providerPresets,providerCatalog,tierVocabulary}.ts`、`src/liveModelCatalog.ts`、`src/modelBudgetRule.ts`、`src/sessionModelLatch.ts`、`src/effortWire.ts` |
|
|
141
141
|
| 12 | **workflow 与后台工作视图** | 15 | `projectWorkflowRun` · `createLiveWorkflowSource` · `createBackgroundView` · `projectBackgroundView` · `recordWorkflowAgentTaskId` · `agentDisplayStatus` | 活过一个 turn 的长任务读面:workflow run + 跨 session 后台任务归一表(`assistant.tasks` 与 fleet SSE **两源独立降级**) | `src/workflow.ts`、`src/workflowClient.ts`、`src/workflowMonitor.ts`、`src/agentSession/backgroundView.ts`(+ 纯类型 `src/agentSession/contract.ts`) |
|
|
142
142
|
| 13 | **座位 IPC 契约** | 33 | `LOCAL_SESSIONS_SPEC` · `SEAT_METHOD_NAMES` · `SEAT_EVENT_TYPES` · `isLocalSessionEvent` · `isToolPermissionRequest` · `toolPermissionRequestId` · `SEAT_VALIDATOR_KEY_COVERAGE` | desktop↔web 座位 IPC 契约的**单一真源**(此前两边各一份、名字零重合 ⇒ 编译器永远不会告诉你它们漂了)。🔴 加 verb 忘了加 `LOCAL_SESSIONS_SPEC` **不报错**:preload 不注册 channel、渲染端读到 `undefined` | `src/seatContract.ts`(**零 import**,纯类型 + 常量 + 纯谓词) |
|
|
143
|
-
| 14 | **宿主端口与会话槽** |
|
|
143
|
+
| 14 | **宿主端口与会话槽** | 26 | `installHost` · `installHostFor` · `hostPortMisses(For)` · `DEFAULT_SESSION_KEY` · `hostEnv` · `unrefTimer` · `parseLocaleTag` / `pickUiLanguage`(#244 F4 族D A-028.20:locale tag 手术单源 + UI 语言判定;与 `resolveRegionHint` 双出口成文 —— 语言偏好域 en/zh ≠ 地址可达域 cn/intl/unknown,`zh-Hant` 前者 zh 后者 intl 是设计)· `engineNamespaceKeyFor` / `mergeSessionMapRecord` / `mergeEngineEntry`(A-028.12:会话 id 映射单一键形 + merge 判定;存储经 `SessionMapStorePort` 归端 —— cli 文件锁/原子写,web localStorage)| 进程/端级装配层(settings/fs/queue/timers/session/log/probe),与 per-turn 的 `AdapterContext` **分层**。头注的判定规则:**这个能力每 turn 都会变吗?** 会 ⇒ `ctx`;不会 ⇒ `installHost` | `src/host.ts`、`src/hostEnv.ts`、`src/sessionSlot.ts`、`src/unrefTimer.ts`、`src/env/{localeGeo,localeTag,uiLanguage}.ts`、`src/sessionMap.ts` |
|
|
144
144
|
| 15 | **控制面与传输** | 68 | `ControlRouter`(+ `ControlSafetyError`)· `makeEngineWireClient` + `resolveWireAuth` · `installEngineWireTarget` · `diagnoseSseIdleTear` / `isSseIdleError` · `attemptActiveRunSelfHeal` + `activeRunBusySignal` + `activeRunSelfHealRow` / `activeRunBusyHeadlessRow` · `kickEngineCapsProbe` / `engineCapTrue` · `mapBrainStatusToRetry` · `waitForClaimRelease` + `CLAIM_RELEASED_STATES` / `CLAIM_HELD_STATES` · `atMostOnceFailureClass` / `readSteerDelivery` · `INTERACTIVE_WAY_OUT`(默认出路串单源)· `normalizeWirePrincipal`(A-028.10:principal 在场性 trim 原语 —— 全空白=缺席不发头,engineWireTarget 两臂/makeEngineWireClient/壳 livePrincipal 同尺)· `classifyTurnWireError` / `isWireTransportError` / `isPreStreamDrainingReject` / `isResumeAtRejection` / `drainingRetryDelayMs` / `scenarioDenyFromError` + `WIRE_NETWORK_ERROR_PATTERN`(A-028.11/.13:turn 错误分型判定半场,人话文案与渲染归端) | 上行通道的**监管**半场(submit / steer / kill / 队列命令定序)+ 传输构造、caps 探测、SSE 断流分诊、**409 active-run 自愈** | `src/controlRouter.ts`、`steering.ts`、`sseIdleTriage.ts`、`retryStatus.ts`、`diagnostics.ts`、`engineWireSdk.ts`、`engineWireTarget.ts`、`src/principalWire.ts`、`src/wireErrorTriage.ts`、`engineSessionParam.ts`、`engineCapsCache.ts`、`liveInitToolFace.ts`、`adapter/activeRunSelfHeal.ts` |
|
|
145
145
|
| 16 | **引擎词汇表与包自检** | 39 | `CONFIG_REFUSAL_CODES` / `isConfigRefusalCode` · `STOP_CONFLICT_CODES` · `isInterruptedToolEndCode` · `isRewindFamilyCode` · `CLIENT_VERBS` · `compensationSplitViolations` | 三端分臂共用的**去字面化** `errorCode` 词表(病根正是三端各抄一份字面);编译期 verb 闭合门;搬迁补偿登记表 | `src/engineErrorCodes.ts`(32 项;A-028.11/.13 补 `DRAINING_ERROR_CODE`/`SCENARIO_NOT_ALLOWED_ERROR_CODE`/`RESUME_AT_ERROR_CODE_PREFIX`)、`src/classifierVerdictWire.ts`、`src/compensations.ts`、`src/clientSlice.ts` |
|
|
146
146
|
|
|
@@ -414,6 +414,7 @@ durable park 腿走 `HitlBridge.decideTool(outcome, toolUseID, opts, preResolved
|
|
|
414
414
|
| `installHitlHostSurface(For)` | `src/hitl/hitlHostSurface.ts` | 通知不上屏(与壳原文 `if (!store) return` 同语义) | **是**(`hitlHostSurfaceMisses()` 恒应为 0) | §4a 的两条**安全告知**(remember-not-applied / edit-not-forwarded)+ cancel-by-deny warn + Recent Denials 记账**全丢** |
|
|
415
415
|
| `installEngineWireTarget(For)` | `src/engineWireTarget.ts` | 回落 env 派生 | — | 🔴 **非 Node 宿主(web / desktop 渲染进程)没有 env** ⇒ 根本拿不到引擎目标。对它们这是**唯一**入口(设了就压过 env);不装 ⇒ `engineRowStopGate` 等按 `baseUrl` 查 caps 的门恒判假。⚠️ **凭证面有边界**:`EngineWireTarget.token` 今天是 `string`,**装不进** `{ mode: 'same-origin-relay' }` —— 浏览器同源宿主必读 **§5d** 与缺口 **P-28** |
|
|
416
416
|
| `installSubagentActivitySink` | `src/subagent/engineSubagentTail.ts` | Subagent Progress 段无数据 | 否 | 明确**不是**错误态:那个面就是没启用 |
|
|
417
|
+
| `installSubagentTailMetaSink` | `src/subagent/engineSubagentTail.ts`(#280 件2,0.30.4) | tail `event: meta` 帧不发布(0.30.3 现状) | 否 | `contentFrames` 供给形判别位(闭集 {on, progress_only, unknown})不达端 ⇒ 端把「结构性无内容(forward 缺席宿主)」误读成「还没来」永远空挂。载荷 = `{taskId, meta}`(meta 为帧**开集原文**,判别位窄读归端);⚠️ 判别位要在子代存活窗内消费(server 侧登记随句柄终态撤销,终态后 tail 读到 `unknown` 不是回归) |
|
|
417
418
|
| `installWorkflowStatusProbe` | `src/notifications.ts` | watcher 没有带对 baseUrl/token/principal 的 `workflows.get` | — | **workflow 完成通知永远不落地** |
|
|
418
419
|
| `installBgTaskStatusProbe` | `src/notifications.ts` | bg 状态 watcher 空转 | — | **后台子代完成通知永远不落地**(这个 watcher 存在的理由正是 idle 期推送不可靠) |
|
|
419
420
|
| `CatalogFetchJson`(注入,非 install) | `src/model/catalog.ts` | 线上腿**整条不启用**(`online.reason = 'no-fetch-port'`) | — | 目录只走包内兜底(端可据 `source` + `online.reason` 诚实渲「内置版本(离线)」) |
|
|
@@ -786,6 +787,7 @@ reason 里写明「枚举器盲区形」。已知两形:
|
|
|
786
787
|
- [ ] `kickEngineCapsProbe(baseUrl, probe)`(caps 门的数据源)
|
|
787
788
|
- [ ] `installWorkflowStatusProbe` / `installBgTaskStatusProbe` —— 不装 = **workflow / 后台子代的完成通知永远不落地**
|
|
788
789
|
- [ ] `installSubagentActivitySink`(可选:Subagent Progress 段的数据源;不装不是错误态)
|
|
790
|
+
- [ ] `installSubagentTailMetaSink`(可选,0.30.4 #280 件2:tail meta 帧的 `contentFrames` 判别位数据源;不装不是错误态,但查看态会把「结构性无内容」渲成永远的「还没来」)
|
|
789
791
|
- [ ] **启动校验用存在性读口**(§5a 的 (a) 表,🔴 **哨兵逐口不同,逐个照抄别推广**;多会话宿主用 `*For` 那一支):`hasApprovalCardPort() === true` · `hasApprovalCardPortFor(key) === true` · **`hitlHostSurfaceFor(key) !== null`**(哨兵是 `null`,写 `!== undefined` **恒真** = 假绿) · `hostSettings() !== undefined` · `hostSettingsFor(key) !== undefined` · `hostFs() !== undefined` · `hostFsFor(key) !== undefined` · `hostSession() !== undefined` · `hostSessionFor(key) !== undefined` · `engineWireTarget() !== null` · `engineWireTargetFor(key) !== null`(`hostTimers()` / `hostTimersFor(key)` 同为 `!== undefined`,按你真装的挑)
|
|
790
792
|
- [ ] **miss 计数当回归探针,不当装配自检**(#252 复审 R3/R4,§5a (b) / 缺口 **P-29**):`hostPortMisses()` 空 / `notificationQueuePortMisses() === 0` / `approvalCardPortMisses() === 0` / `hitlHostSurfaceMisses() === 0` / `compensationSplitViolations()` 空 —— 🔴 **这几个数初值就是 0/空**,「完全没装口 + 还没发生任何调用」时它们照样全绿,**启动瞬间的断言证明不了装配**。正确用法 = 跑过**一轮真流量**之后再断言它们仍为 0。⚠️ **通知队列口今天没有存在性读口**(P-29),那一条只能靠你自己记得调过
|
|
791
793
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.5",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|