@sema-agent/client-core 0.62.1 → 0.63.0

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