@sema-agent/client-core 0.61.0 → 0.62.1
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 +160 -0
- package/README.md +8 -2
- package/dist/adapt/arms.js +76 -13
- package/dist/adapt/textStream.d.ts +31 -2
- package/dist/adapt/textStream.js +12 -1
- package/dist/adapter/runStream.js +16 -0
- package/dist/agentsWireCaps.d.ts +6 -2
- package/dist/engineWireSdk.d.ts +23 -2
- package/dist/engineWireSdk.js +71 -4
- package/dist/fleetTaskDesc.d.ts +19 -0
- package/dist/fleetTaskDesc.js +34 -0
- package/dist/interactiveHalt.d.ts +40 -17
- package/dist/interactiveHalt.js +32 -20
- package/dist/readFacePosture.js +1 -28
- package/dist/seam.d.ts +24 -1
- package/dist/seam.js +7 -0
- package/dist/seatContract.d.ts +84 -1
- package/dist/seatContract.js +37 -0
- package/dist/sqlEngineCapability.d.ts +2 -2
- package/dist/sqlEngineCapability.js +13 -14
- package/dist/subagentContentStore.d.ts +74 -1
- package/dist/subagentContentStore.js +539 -26
- package/dist/writeProtectionCapability.d.ts +4 -0
- package/dist/writeProtectionCapability.js +7 -3
- package/docs/INTEGRATION-CLIENTS.md +392 -7
- package/package.json +1 -1
package/dist/engineWireSdk.js
CHANGED
|
@@ -67,6 +67,57 @@ export function resolveWireAuth(baseUrl, token) {
|
|
|
67
67
|
export function wireAuthTokenFor(baseUrl, token) {
|
|
68
68
|
return resolveWireAuth(baseUrl, token);
|
|
69
69
|
}
|
|
70
|
+
/** 出站头的**归一读写**(SDK 的传输层给的是普通对象,但入参形状是 `HeadersInit` 三形之一)。 */
|
|
71
|
+
function headersToRecord(init) {
|
|
72
|
+
const out = {};
|
|
73
|
+
if (init === undefined)
|
|
74
|
+
return out;
|
|
75
|
+
if (Array.isArray(init)) {
|
|
76
|
+
for (const [k, v] of init)
|
|
77
|
+
out[String(k).toLowerCase()] = String(v);
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
const maybe = init;
|
|
81
|
+
if (typeof maybe.forEach === 'function') {
|
|
82
|
+
;
|
|
83
|
+
init.forEach((v, k) => {
|
|
84
|
+
out[String(k).toLowerCase()] = String(v);
|
|
85
|
+
});
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
for (const [k, v] of Object.entries(init))
|
|
89
|
+
out[String(k).toLowerCase()] = String(v);
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 把一个 `fetch` 包一层:**每一发出站请求**重读凭证并就地改写 Authorization 头。
|
|
94
|
+
*
|
|
95
|
+
* 🔴 为什么是包 `fetch` 而不是「构造时解析一次」:底层客户端把凭证捕获进构造期闭包,构造时解析
|
|
96
|
+
* 等于把快照换了个位置放 —— 换代之后照样发旧凭证。请求那一刻才是**决定结果的时刻**。
|
|
97
|
+
* 🔴 三态在这一层逐格重演(与 {@link resolveWireAuth} 同一只):串 ⇒ `Bearer <串>`;
|
|
98
|
+
* `{mode:'loopback-unauthed'}` ⇒ **删**掉 Authorization 头(绝不伪造一个)。
|
|
99
|
+
* 🔴 取值函数抛错 ⇒ 当「这一刻没有凭证」处置(fail-soft):一个坏掉的取值口不该让整条 wire 不可用,
|
|
100
|
+
* 而且异常里可能夹带凭证材料,原样上抛就是又一条外溢面。
|
|
101
|
+
*/
|
|
102
|
+
function withPerRequestAuth(baseUrl, read, inner) {
|
|
103
|
+
return async (input, init) => {
|
|
104
|
+
let token;
|
|
105
|
+
try {
|
|
106
|
+
token = read();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// fail-soft:取不到就按无 token 走三态(理由见本函数头注第三条)。
|
|
110
|
+
token = undefined;
|
|
111
|
+
}
|
|
112
|
+
const resolved = resolveWireAuth(baseUrl, token);
|
|
113
|
+
const headers = headersToRecord(init?.headers);
|
|
114
|
+
if (typeof resolved === 'string')
|
|
115
|
+
headers.authorization = `Bearer ${resolved}`;
|
|
116
|
+
else
|
|
117
|
+
delete headers.authorization;
|
|
118
|
+
return inner(input, { ...init, headers });
|
|
119
|
+
};
|
|
120
|
+
}
|
|
70
121
|
/**
|
|
71
122
|
* 构造一个引擎 wire 用 AgentClient。null = 构造失败(SDK fail-closed 拒绝——不该发生:
|
|
72
123
|
* wireAuthTokenFor 恒给合法形;防御性 fail-soft,调用方按各自离线/探测失败路径处理)。
|
|
@@ -74,6 +125,9 @@ export function wireAuthTokenFor(baseUrl, token) {
|
|
|
74
125
|
export function makeEngineWireClient(cfg) {
|
|
75
126
|
try {
|
|
76
127
|
const principal = normalizeWirePrincipal(cfg.principal);
|
|
128
|
+
// 取值函数形:出站腿包一层「每发一次读一次」;构造期**一次都不读**(读一次就是一次快照)。
|
|
129
|
+
const source = typeof cfg.token === 'function' ? cfg.token : null;
|
|
130
|
+
const innerFetch = cfg.fetchImpl ?? fetch;
|
|
77
131
|
const base = {
|
|
78
132
|
baseUrl: cfg.baseUrl,
|
|
79
133
|
// F-011 停发:缺席/空串=不给键(SDK 6.11 缺席=不发 x-agent-principal 头,owner-null)。
|
|
@@ -81,13 +135,26 @@ export function makeEngineWireClient(cfg) {
|
|
|
81
135
|
...(principal !== undefined ? { principal } : {}),
|
|
82
136
|
...(cfg.timeoutMs !== undefined ? { timeoutMs: cfg.timeoutMs } : {}),
|
|
83
137
|
maxRetries: cfg.maxRetries ?? 0,
|
|
84
|
-
...(
|
|
138
|
+
...(source !== null
|
|
139
|
+
? { fetch: withPerRequestAuth(cfg.baseUrl, source, innerFetch) }
|
|
140
|
+
: cfg.fetchImpl
|
|
141
|
+
? { fetch: cfg.fetchImpl }
|
|
142
|
+
: {}),
|
|
85
143
|
};
|
|
86
144
|
// relay 形直传(显式声明,不过三态解析);串/缺席走 resolveWireAuth 三态,语义与 0.28.x 字节
|
|
87
145
|
// 不变。分支构造而非三元合流:SDK AgentClientConfig 按 authToken 判别联合,联合值不可直赋。
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
146
|
+
if (typeof cfg.token === 'object')
|
|
147
|
+
return new AgentClient({ ...base, authToken: cfg.token });
|
|
148
|
+
if (source !== null) {
|
|
149
|
+
// 🔴 构造期给的是**不读取值函数**的三态形(回环 ⇒ loopback-unauthed;非回环 ⇒ 匿名身份)。
|
|
150
|
+
// 它只用来过 SDK 的构造期形校验与浏览器守卫 —— 真正上 wire 的那一份由上面那层每发重写,
|
|
151
|
+
// 所以这里绝不能顺手调一次取值函数:调了就多一次「与任何请求都不对应」的读取,
|
|
152
|
+
// 而「读取次数 = 请求次数」正是本形的可验证承诺(见 run-wire-auth-source-test.mjs G2)。
|
|
153
|
+
return new AgentClient({ ...base, authToken: resolveWireAuth(cfg.baseUrl, undefined) });
|
|
154
|
+
}
|
|
155
|
+
// 到这里 `cfg.token` 只可能是串或缺席(对象形与函数形都已在上面分流),类型面收窄靠一次显式判。
|
|
156
|
+
const literal = typeof cfg.token === 'string' ? cfg.token : undefined;
|
|
157
|
+
return new AgentClient({ ...base, authToken: resolveWireAuth(cfg.baseUrl, literal) });
|
|
91
158
|
}
|
|
92
159
|
catch {
|
|
93
160
|
return null;
|
package/dist/fleetTaskDesc.d.ts
CHANGED
|
@@ -6,6 +6,25 @@
|
|
|
6
6
|
* 供单测直取——fleetClient 拖 SDK/appState/notification 重图,无法轻量 bundle;行为与提出前逐字同源)。
|
|
7
7
|
*/
|
|
8
8
|
export declare function escapeDisplayControlChars(s: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* UNTRUSTED-for-display 呈前处理的**共用铸点**:**先转义、后按转义结果封长**。
|
|
11
|
+
*
|
|
12
|
+
* 🔴 顺序反过来会让承诺的显示预算失守 —— {@link escapeDisplayControlChars} 把每个不可见字符
|
|
13
|
+
* 改写成 6 字符的 `\uXXXX`,先按原文截长再转义,会把一段纯控制字符的 `max` 个原始字符送成
|
|
14
|
+
* `6 × max` 个显示字符。屏上的列宽预算是按**显示字符**给的,所以封长必须量转义后的那一份。
|
|
15
|
+
* 🔴 相反方向的顾虑(「先转义后截会把一枚转义序列拦腰截断」)由下面两条避让解掉,不构成
|
|
16
|
+
* 改回先截后转义的理由:
|
|
17
|
+
* · 截点落在一枚 `\uXXXX` token 中间 ⇒ 回退到该 token 之前(留半截 `\u00` 比整枚不渲更坏,
|
|
18
|
+
* 它看起来像一个完整答案的开头);
|
|
19
|
+
* · 截点落在一对合法代理对(如 emoji)中间 ⇒ 回退一位。`DISPLAY_UNSAFE` 只转义**孤**代理项,
|
|
20
|
+
* 合法对被放行原样透传,在这里截断等于人为造一枚裸高位代理项留在末尾。
|
|
21
|
+
*
|
|
22
|
+
* 语义:返回值恒是**已消毒**的串,长度 ≤ `max`(可能因避让短于 `max`)。`max <= 0` ⇒ 空串。
|
|
23
|
+
*
|
|
24
|
+
* ⇄ 本函数是本包该族的**唯一**实现(0.62.0 起):operator 面的 READ 档位读器、写保护姿态行、
|
|
25
|
+
* SQL 姿态行四处此前各写一份,其中三份是先截后转义 —— 同形存量清剿后一律接这一只。
|
|
26
|
+
*/
|
|
27
|
+
export declare function capForDisplay(raw: string, max: number): string;
|
|
9
28
|
/** collapse a wire label to a single trimmed line (drops embedded newlines/runs). 187 row labels are always
|
|
10
29
|
* one short line; the <FleetTree/> column (Math.min(28,…) + wrap="truncate") does the visible ellipsis, so we
|
|
11
30
|
* only normalize here — no double-ellipsis, faithful to the 187 render.
|
package/dist/fleetTaskDesc.js
CHANGED
|
@@ -39,6 +39,40 @@ const DISPLAY_UNSAFE = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028\u202
|
|
|
39
39
|
export function escapeDisplayControlChars(s) {
|
|
40
40
|
return s.replace(DISPLAY_UNSAFE, ch => `\\u${(ch.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, '0')}`);
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* UNTRUSTED-for-display 呈前处理的**共用铸点**:**先转义、后按转义结果封长**。
|
|
44
|
+
*
|
|
45
|
+
* 🔴 顺序反过来会让承诺的显示预算失守 —— {@link escapeDisplayControlChars} 把每个不可见字符
|
|
46
|
+
* 改写成 6 字符的 `\uXXXX`,先按原文截长再转义,会把一段纯控制字符的 `max` 个原始字符送成
|
|
47
|
+
* `6 × max` 个显示字符。屏上的列宽预算是按**显示字符**给的,所以封长必须量转义后的那一份。
|
|
48
|
+
* 🔴 相反方向的顾虑(「先转义后截会把一枚转义序列拦腰截断」)由下面两条避让解掉,不构成
|
|
49
|
+
* 改回先截后转义的理由:
|
|
50
|
+
* · 截点落在一枚 `\uXXXX` token 中间 ⇒ 回退到该 token 之前(留半截 `\u00` 比整枚不渲更坏,
|
|
51
|
+
* 它看起来像一个完整答案的开头);
|
|
52
|
+
* · 截点落在一对合法代理对(如 emoji)中间 ⇒ 回退一位。`DISPLAY_UNSAFE` 只转义**孤**代理项,
|
|
53
|
+
* 合法对被放行原样透传,在这里截断等于人为造一枚裸高位代理项留在末尾。
|
|
54
|
+
*
|
|
55
|
+
* 语义:返回值恒是**已消毒**的串,长度 ≤ `max`(可能因避让短于 `max`)。`max <= 0` ⇒ 空串。
|
|
56
|
+
*
|
|
57
|
+
* ⇄ 本函数是本包该族的**唯一**实现(0.62.0 起):operator 面的 READ 档位读器、写保护姿态行、
|
|
58
|
+
* SQL 姿态行四处此前各写一份,其中三份是先截后转义 —— 同形存量清剿后一律接这一只。
|
|
59
|
+
*/
|
|
60
|
+
export function capForDisplay(raw, max) {
|
|
61
|
+
const escaped = escapeDisplayControlChars(raw);
|
|
62
|
+
if (escaped.length <= max)
|
|
63
|
+
return escaped;
|
|
64
|
+
let cut = max;
|
|
65
|
+
const tokenStart = escaped.lastIndexOf('\\', cut - 1);
|
|
66
|
+
if (tokenStart >= 0 && tokenStart + 6 > cut && /^\\u[0-9A-F]{4}$/.test(escaped.slice(tokenStart, tokenStart + 6))) {
|
|
67
|
+
cut = tokenStart;
|
|
68
|
+
}
|
|
69
|
+
if (cut > 0) {
|
|
70
|
+
const code = escaped.charCodeAt(cut - 1);
|
|
71
|
+
if (code >= 0xd800 && code <= 0xdbff)
|
|
72
|
+
cut -= 1;
|
|
73
|
+
}
|
|
74
|
+
return cut > 0 ? escaped.slice(0, cut) : '';
|
|
75
|
+
}
|
|
42
76
|
/** collapse a wire label to a single trimmed line (drops embedded newlines/runs). 187 row labels are always
|
|
43
77
|
* one short line; the <FleetTree/> column (Math.min(28,…) + wrap="truncate") does the visible ellipsis, so we
|
|
44
78
|
* only normalize here — no double-ellipsis, faithful to the 187 render.
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* interactiveHalt — 交互 **Esc** 的停止判定层(#363 件③,0.47.0;三端公共判定上收)。
|
|
3
3
|
*
|
|
4
4
|
* ── 收的是哪一件事 ────────────────────────────────────────────────────────────────────────
|
|
5
|
-
* 「用户按 Esc ⇒ 先发 **turn 级 halt**;升级成 **run 级 cancel**
|
|
5
|
+
* 「用户按 Esc ⇒ 先发 **turn 级 halt**;升级成 **run 级 cancel** 恰有**三格**:
|
|
6
6
|
* ① 引擎自己回了升级闭集里的 **409**(它在说「这里没有在飞 turn 可切,run 级停止请用 cancel」);
|
|
7
|
-
* ② 这一发**连判决都没拿到**(传输失败/超时/未武装)**且**屏上确实挂着审批卡(`parked`)
|
|
7
|
+
* ② 这一发**连判决都没拿到**(传输失败/超时/未武装)**且**屏上确实挂着审批卡(`parked`);
|
|
8
|
+
* ③ 这一发连判决都没拿到 **且** 这条 run 武装了 **detach**(断连不再收 run,0.62.0 新格)。
|
|
8
9
|
* 其余一律不升级。」—— 这条判定 TUI / desktop / web **三端都要**(三端都会 Esc、都会撞
|
|
9
10
|
* 同一个 parked 格),此前整条住在 cli 壳里(`src/sema/seamQuery.ts` 的 `bestEffortInteractiveHalt`
|
|
10
11
|
* + `src/sema/interruptWire.ts` 的 `needsRunLevelStop`)。本模块把**判据**搬进来;
|
|
@@ -96,6 +97,20 @@ export interface InteractiveHaltInput {
|
|
|
96
97
|
* 🔴 缺席 / 非 `true` 一律按**非 parked** 处理(fail-closed:证不出 parked 就不给升级资格)。
|
|
97
98
|
*/
|
|
98
99
|
readonly parked?: boolean | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* **这条 run 武装了 detach 吗**(壳自己独立知道的第二个事实:出站时给了「断连不收 run」的那一位)。
|
|
102
|
+
*
|
|
103
|
+
* 🔴 为什么它会改变判决(0.62.0 新格):撕 SSE 的**后果**因它而变。老形态下交互车道零 detach ——
|
|
104
|
+
* 撕流 = server 按断连语义当场把那条 run 收尾,所以「interrupt 那一发没落地」还有一层兜底
|
|
105
|
+
* (用户按 Esc、壳撕流,run 也就停了)。detach 武装之后,撕流只断开**观看**,run 会一路跑到
|
|
106
|
+
* turn 结束:此时 interrupt 自身失败(传输失败/超时/未武装)= 用户按下的 Esc **一点效果都没有**,
|
|
107
|
+
* 而屏上没有任何东西会告诉他这件事。这一格就是那层兜底消失后留下的洞。
|
|
108
|
+
* 🔴 它**只在「连判决都没拿到」那一格**被读 —— 与 {@link InteractiveHaltInput.parked} 同一条纪律:
|
|
109
|
+
* 引擎给了判决时判决说了算,壳自己知道的事实不许骑到引擎的结构化答复上。
|
|
110
|
+
* 🔴 **缺席 / 非 `true` 一律按未武装处理**(fail-closed,与 `parked` 同尺):证不出 detach 就不给
|
|
111
|
+
* 升级资格。⇒ 不传这个键的宿主行为与上一版**逐格相同**(门里有全格穷举的零差断言)。
|
|
112
|
+
*/
|
|
113
|
+
readonly detachArmed?: boolean | undefined;
|
|
99
114
|
/**
|
|
100
115
|
* 首发 interrupt 的结局。**缺席 = 这一发还没打** ⇒ 本函数判 `interrupt`(首发恒行,无条件)。
|
|
101
116
|
* 在场 ⇒ 本函数回答的是「接着还要不要升级」。
|
|
@@ -114,7 +129,9 @@ export type InteractiveHaltReason =
|
|
|
114
129
|
| 'engine-says-run-level'
|
|
115
130
|
/** 连判决都没拿到(传输失败/超时/未武装),但壳自证屏上挂着审批卡 ⇒ 这一格本来就没有在飞 turn。 */
|
|
116
131
|
| 'no-verdict-on-parked-card'
|
|
117
|
-
/**
|
|
132
|
+
/** 连判决都没拿到、也没有审批卡,但这条 run 武装了 detach ⇒ 撕流不会收它,不升级 = Esc 零效果。 */
|
|
133
|
+
| 'no-verdict-detach-armed'
|
|
134
|
+
/** 连判决都没拿到、且两个壳侧事实都不成立 ⇒ 那里真可能有在飞工具,凭一次超时拆整条 run 正是要消除的病。 */
|
|
118
135
|
| 'no-verdict-not-parked'
|
|
119
136
|
/** 引擎给了判决,但不在升级闭集里(404 / 400 / 非 409 / `steering.not_running` / 码读不出)。 */
|
|
120
137
|
| 'refused-no-escalation'
|
|
@@ -129,22 +146,28 @@ export interface InteractiveHaltPlan {
|
|
|
129
146
|
* Esc 停止弧的**唯一判定口**。
|
|
130
147
|
*
|
|
131
148
|
* ── 分支表(逐条 = 一条行为承诺)──────────────────────────────────────────────────────────
|
|
132
|
-
* | `interruptOutcome` | `parked` | 判决 | reason |
|
|
133
|
-
*
|
|
134
|
-
* | 缺席(还没打) | 任意 | `interrupt` | `first-shot` |
|
|
135
|
-
* | `halted` | 任意 | `none` | `halted` |
|
|
136
|
-
* | `refused` + 409 + 升级闭集码 | 任意 | `escalate-cancel` | `engine-says-run-level` |
|
|
137
|
-
* | `refused`(其余:非 409 / 码不在闭集 / 码缺席)| 任意 | `none` | `refused-no-escalation` |
|
|
138
|
-
* | `transport` / `unarmed` | `true` | `escalate-cancel` | `no-verdict-on-parked-card` |
|
|
139
|
-
* | `transport` / `unarmed` | 其余 | `
|
|
140
|
-
* |
|
|
149
|
+
* | `interruptOutcome` | `parked` | `detachArmed` | 判决 | reason |
|
|
150
|
+
* |--------------------------------------------|----------|---------------|-------------------|--------|
|
|
151
|
+
* | 缺席(还没打) | 任意 | 任意 | `interrupt` | `first-shot` |
|
|
152
|
+
* | `halted` | 任意 | 任意 | `none` | `halted` |
|
|
153
|
+
* | `refused` + 409 + 升级闭集码 | 任意 | 任意 | `escalate-cancel` | `engine-says-run-level` |
|
|
154
|
+
* | `refused`(其余:非 409 / 码不在闭集 / 码缺席)| 任意 | 任意 | `none` | `refused-no-escalation` |
|
|
155
|
+
* | `transport` / `unarmed` | `true` | 任意 | `escalate-cancel` | `no-verdict-on-parked-card` |
|
|
156
|
+
* | `transport` / `unarmed` | 其余 | `true` | `escalate-cancel` | `no-verdict-detach-armed` |
|
|
157
|
+
* | `transport` / `unarmed` | 其余 | 其余 | `none` | `no-verdict-not-parked` |
|
|
158
|
+
* | 认不得的形 | 任意 | 任意 | `none` | `unknown-outcome` |
|
|
141
159
|
*
|
|
142
|
-
* 🔴 **`parked` 只在「没有判决」那两格被读**:引擎给了判决时,判决说了算 ——
|
|
143
|
-
*
|
|
160
|
+
* 🔴 **`parked` 与 `detachArmed` 只在「没有判决」那两格被读**:引擎给了判决时,判决说了算 ——
|
|
161
|
+
* 壳的 UI 状态不许覆盖引擎的结构化答复(反过来也一样:引擎说 parked 时,`parked=false` 不阻止升级)。
|
|
162
|
+
* 🔴 **两个壳侧事实的先后是刻意的**:`parked` 先判,所以老宿主(只传 `parked`)的 reason 逐字不变;
|
|
163
|
+
* 两者同时为 `true` 时报 `no-verdict-on-parked-card` —— 那一格的处置与文案本来就成立,新键不改口。
|
|
164
|
+
* 🔴 **`detachArmed` 缺席 = 老语义零差**:不传这个键时整张表与上一版逐格相同(门里有全格穷举断言)。
|
|
144
165
|
* 🔴 **首发无条件**:`first-shot` 那一格刻意不看 `parked`。审批卡挂着时首发 interrupt 会吃一个
|
|
145
166
|
* 409,那正是升级闸要的**判决**;为了省一次往返而直接跳到 cancel,等于把判据从引擎搬回壳里猜。
|
|
146
|
-
* 🔴 **顺序契约(端必读,不是本函数能保证的那半)**:这一发必须排在「撕 SSE
|
|
147
|
-
*
|
|
148
|
-
*
|
|
167
|
+
* 🔴 **顺序契约(端必读,不是本函数能保证的那半)**:这一发必须排在「撕 SSE」**之前**。
|
|
168
|
+
* · **未武装 detach** 的车道上,先撕流 = server 按断连语义当场收尾那条 run,随后落地的 interrupt
|
|
169
|
+
* 只会拿到 409 `steering.not_running`(真机实测:同步撕流形每一轮都是它)。
|
|
170
|
+
* · **武装了 detach** 的车道上,撕流只断开观看、run 照跑 —— 顺序不再影响 interrupt 拿到什么,
|
|
171
|
+
* 但它让「interrupt 没落地」变成一个**没有兜底**的结局,那正是 `detachArmed` 这一格要接的洞。
|
|
149
172
|
*/
|
|
150
173
|
export declare function planInteractiveHalt(input: InteractiveHaltInput): InteractiveHaltPlan;
|
package/dist/interactiveHalt.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* interactiveHalt — 交互 **Esc** 的停止判定层(#363 件③,0.47.0;三端公共判定上收)。
|
|
3
3
|
*
|
|
4
4
|
* ── 收的是哪一件事 ────────────────────────────────────────────────────────────────────────
|
|
5
|
-
* 「用户按 Esc ⇒ 先发 **turn 级 halt**;升级成 **run 级 cancel**
|
|
5
|
+
* 「用户按 Esc ⇒ 先发 **turn 级 halt**;升级成 **run 级 cancel** 恰有**三格**:
|
|
6
6
|
* ① 引擎自己回了升级闭集里的 **409**(它在说「这里没有在飞 turn 可切,run 级停止请用 cancel」);
|
|
7
|
-
* ② 这一发**连判决都没拿到**(传输失败/超时/未武装)**且**屏上确实挂着审批卡(`parked`)
|
|
7
|
+
* ② 这一发**连判决都没拿到**(传输失败/超时/未武装)**且**屏上确实挂着审批卡(`parked`);
|
|
8
|
+
* ③ 这一发连判决都没拿到 **且** 这条 run 武装了 **detach**(断连不再收 run,0.62.0 新格)。
|
|
8
9
|
* 其余一律不升级。」—— 这条判定 TUI / desktop / web **三端都要**(三端都会 Esc、都会撞
|
|
9
10
|
* 同一个 parked 格),此前整条住在 cli 壳里(`src/sema/seamQuery.ts` 的 `bestEffortInteractiveHalt`
|
|
10
11
|
* + `src/sema/interruptWire.ts` 的 `needsRunLevelStop`)。本模块把**判据**搬进来;
|
|
@@ -85,23 +86,29 @@ function enginePointsToRunLevelStop(outcome) {
|
|
|
85
86
|
* Esc 停止弧的**唯一判定口**。
|
|
86
87
|
*
|
|
87
88
|
* ── 分支表(逐条 = 一条行为承诺)──────────────────────────────────────────────────────────
|
|
88
|
-
* | `interruptOutcome` | `parked` | 判决 | reason |
|
|
89
|
-
*
|
|
90
|
-
* | 缺席(还没打) | 任意 | `interrupt` | `first-shot` |
|
|
91
|
-
* | `halted` | 任意 | `none` | `halted` |
|
|
92
|
-
* | `refused` + 409 + 升级闭集码 | 任意 | `escalate-cancel` | `engine-says-run-level` |
|
|
93
|
-
* | `refused`(其余:非 409 / 码不在闭集 / 码缺席)| 任意 | `none` | `refused-no-escalation` |
|
|
94
|
-
* | `transport` / `unarmed` | `true` | `escalate-cancel` | `no-verdict-on-parked-card` |
|
|
95
|
-
* | `transport` / `unarmed` | 其余 | `
|
|
96
|
-
* |
|
|
89
|
+
* | `interruptOutcome` | `parked` | `detachArmed` | 判决 | reason |
|
|
90
|
+
* |--------------------------------------------|----------|---------------|-------------------|--------|
|
|
91
|
+
* | 缺席(还没打) | 任意 | 任意 | `interrupt` | `first-shot` |
|
|
92
|
+
* | `halted` | 任意 | 任意 | `none` | `halted` |
|
|
93
|
+
* | `refused` + 409 + 升级闭集码 | 任意 | 任意 | `escalate-cancel` | `engine-says-run-level` |
|
|
94
|
+
* | `refused`(其余:非 409 / 码不在闭集 / 码缺席)| 任意 | 任意 | `none` | `refused-no-escalation` |
|
|
95
|
+
* | `transport` / `unarmed` | `true` | 任意 | `escalate-cancel` | `no-verdict-on-parked-card` |
|
|
96
|
+
* | `transport` / `unarmed` | 其余 | `true` | `escalate-cancel` | `no-verdict-detach-armed` |
|
|
97
|
+
* | `transport` / `unarmed` | 其余 | 其余 | `none` | `no-verdict-not-parked` |
|
|
98
|
+
* | 认不得的形 | 任意 | 任意 | `none` | `unknown-outcome` |
|
|
97
99
|
*
|
|
98
|
-
* 🔴 **`parked` 只在「没有判决」那两格被读**:引擎给了判决时,判决说了算 ——
|
|
99
|
-
*
|
|
100
|
+
* 🔴 **`parked` 与 `detachArmed` 只在「没有判决」那两格被读**:引擎给了判决时,判决说了算 ——
|
|
101
|
+
* 壳的 UI 状态不许覆盖引擎的结构化答复(反过来也一样:引擎说 parked 时,`parked=false` 不阻止升级)。
|
|
102
|
+
* 🔴 **两个壳侧事实的先后是刻意的**:`parked` 先判,所以老宿主(只传 `parked`)的 reason 逐字不变;
|
|
103
|
+
* 两者同时为 `true` 时报 `no-verdict-on-parked-card` —— 那一格的处置与文案本来就成立,新键不改口。
|
|
104
|
+
* 🔴 **`detachArmed` 缺席 = 老语义零差**:不传这个键时整张表与上一版逐格相同(门里有全格穷举断言)。
|
|
100
105
|
* 🔴 **首发无条件**:`first-shot` 那一格刻意不看 `parked`。审批卡挂着时首发 interrupt 会吃一个
|
|
101
106
|
* 409,那正是升级闸要的**判决**;为了省一次往返而直接跳到 cancel,等于把判据从引擎搬回壳里猜。
|
|
102
|
-
* 🔴 **顺序契约(端必读,不是本函数能保证的那半)**:这一发必须排在「撕 SSE
|
|
103
|
-
*
|
|
104
|
-
*
|
|
107
|
+
* 🔴 **顺序契约(端必读,不是本函数能保证的那半)**:这一发必须排在「撕 SSE」**之前**。
|
|
108
|
+
* · **未武装 detach** 的车道上,先撕流 = server 按断连语义当场收尾那条 run,随后落地的 interrupt
|
|
109
|
+
* 只会拿到 409 `steering.not_running`(真机实测:同步撕流形每一轮都是它)。
|
|
110
|
+
* · **武装了 detach** 的车道上,撕流只断开观看、run 照跑 —— 顺序不再影响 interrupt 拿到什么,
|
|
111
|
+
* 但它让「interrupt 没落地」变成一个**没有兜底**的结局,那正是 `detachArmed` 这一格要接的洞。
|
|
105
112
|
*/
|
|
106
113
|
export function planInteractiveHalt(input) {
|
|
107
114
|
const outcome = input.interruptOutcome;
|
|
@@ -121,10 +128,15 @@ export function planInteractiveHalt(input) {
|
|
|
121
128
|
: { action: 'none', reason: 'refused-no-escalation' };
|
|
122
129
|
}
|
|
123
130
|
if (kind === 'transport' || kind === 'unarmed') {
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
// 没拿到判决。只有壳能**独立证明**的两个事实之一成立才升级(理由见顶注的不对称段)。
|
|
132
|
+
// 🔴 `parked` 先判:老宿主只传它,先判保证那一格的 reason 逐字不漂。
|
|
133
|
+
if (input.parked === true)
|
|
134
|
+
return { action: 'escalate-cancel', reason: 'no-verdict-on-parked-card' };
|
|
135
|
+
// 🔴 detach 武装 ⇒ 撕流不再收 run,不升级 = 用户按下的 Esc 一点效果都没有。
|
|
136
|
+
// 严格 `=== true`(与 parked 同尺):证不出来就不给升级资格。
|
|
137
|
+
if (input.detachArmed === true)
|
|
138
|
+
return { action: 'escalate-cancel', reason: 'no-verdict-detach-armed' };
|
|
139
|
+
return { action: 'none', reason: 'no-verdict-not-parked' };
|
|
128
140
|
}
|
|
129
141
|
// 宿主传了本模块认不得的形(新 kind / 坏对象)。fail-closed:不对一个读不懂的结局动手。
|
|
130
142
|
return { action: 'none', reason: 'unknown-outcome' };
|
package/dist/readFacePosture.js
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
2
|
/** `note` 上屏前的封长(UTF-16 单元,按**转义后**的字节数算——见 {@link capForDisplay})。它是
|
|
3
3
|
* 一句人话不是一个词,给得比 source/face 宽。 */
|
|
4
4
|
const READ_FACE_NOTE_MAX = 256;
|
|
5
5
|
/** `source` / 分歧句里词形的封长(与本包其余 detail 铸点同值同理由;同样按转义后字节数算)。 */
|
|
6
6
|
const READ_FACE_WORD_MAX = 40;
|
|
7
|
-
/**
|
|
8
|
-
* UNTRUSTED-for-display 呈前处理:**先转义、后按转义结果封长**——顺序反过来会让预算失守
|
|
9
|
-
* (`escapeDisplayControlChars` 把每个不可见字符改写成 6 字符的 `\uXXXX`,截长在前会把一段纯
|
|
10
|
-
* 控制字符的原文送成六倍长的显示串;文件头注有完整推导)。
|
|
11
|
-
*
|
|
12
|
-
* 封长点额外避开两种会留下断裂片段的位置:
|
|
13
|
-
* · 一枚 `escapeDisplayControlChars` 铸出的 `\uXXXX` token 中间(留半截 `\u00` 比整枚不渲更坏 ——
|
|
14
|
-
* 它看起来像一个完整答案的开头);
|
|
15
|
-
* · 一对合法代理对(如 emoji)中间——`DISPLAY_UNSAFE` 只转义**孤**代理项,合法对被放行原样透传,
|
|
16
|
-
* 在这里截断会人为地把它拆成一枚裸高位代理项留在末尾。
|
|
17
|
-
*/
|
|
18
|
-
function capForDisplay(raw, max) {
|
|
19
|
-
const escaped = escapeDisplayControlChars(raw);
|
|
20
|
-
if (escaped.length <= max)
|
|
21
|
-
return escaped;
|
|
22
|
-
let cut = max;
|
|
23
|
-
const tokenStart = escaped.lastIndexOf('\\', cut - 1);
|
|
24
|
-
if (tokenStart >= 0 && tokenStart + 6 > cut && /^\\u[0-9A-F]{4}$/.test(escaped.slice(tokenStart, tokenStart + 6))) {
|
|
25
|
-
cut = tokenStart;
|
|
26
|
-
}
|
|
27
|
-
if (cut > 0) {
|
|
28
|
-
const code = escaped.charCodeAt(cut - 1);
|
|
29
|
-
if (code >= 0xd800 && code <= 0xdbff)
|
|
30
|
-
cut -= 1;
|
|
31
|
-
}
|
|
32
|
-
return escaped.slice(0, cut);
|
|
33
|
-
}
|
|
34
7
|
/**
|
|
35
8
|
* `wiring.readFace` → 读数;**畸形一律 `undefined`**,绝不抛出。
|
|
36
9
|
*
|
package/dist/seam.d.ts
CHANGED
|
@@ -390,7 +390,7 @@ export type ChromeEvent = {
|
|
|
390
390
|
* `actor.hostAsserted` 是消费端唯一能判「这个署名可信吗」的位:渲署名而不渲这个位 = 把一个
|
|
391
391
|
* 未经验证的名字渲成可信的(core 自己的 `[from …]` 渲染就是靠它决定加不加 `(unverified)`)。
|
|
392
392
|
*/
|
|
393
|
-
| HumanInputChromeEvent | EngineNoticeChromeEvent | TextSegmentEndChromeEvent | WiringManifestChromeEvent;
|
|
393
|
+
| HumanInputChromeEvent | EngineNoticeChromeEvent | TextSegmentEndChromeEvent | WiringManifestChromeEvent | ResultTextDivergedChromeEvent;
|
|
394
394
|
/**
|
|
395
395
|
* {@link ChromeEvent} 的 `wiring_manifest` 臂(core #524 + core 147③,server ≥7.58.0)——
|
|
396
396
|
* 引擎接线自述里**三段面向终端用户的事实**(S-124 起 `mcp[]` 是第三段),其余每一段仍不投影(射程见 eventToSdkMessage 的
|
|
@@ -480,6 +480,29 @@ export interface TextSegmentEndChromeEvent {
|
|
|
480
480
|
/** core 铸的事件身份(uuidv7 形);wire 未必带 ⇒ 缺席时本键不在场。 */
|
|
481
481
|
eventId?: string;
|
|
482
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* {@link ChromeEvent} 的 `result_text_diverged` 臂(0.62.0)——「**终帧正文与已上屏的正文对不上**」。
|
|
485
|
+
*
|
|
486
|
+
* 什么时候会有:一个 turn 的正文正常是**流着上屏**的,终帧带的那份是同一段话的全文,两者恒相等
|
|
487
|
+
* (或终帧那份是已上屏的**延长**——断线期间模型接着说完了,那一截由库补吐一发)。本臂只在第三种
|
|
488
|
+
* 情形发出:两份**互相都不是对方的前缀** —— 即终帧说的话与屏上那段**从某处起就分了岔**。
|
|
489
|
+
*
|
|
490
|
+
* 🔴 **库刻意什么都不补吐**:两份对不上时,谁才算数**只有引擎说得清**;把终帧那份接在屏上已有
|
|
491
|
+
* 那段后面,会拼出一段**谁都没说过**的话(前半是流式的、后半是另一版的),那比缺一截更坏。
|
|
492
|
+
* 🔴 **宿主消费义务**(可选、fail-soft):把这一拍的转录行**改口** —— 一句诚实的「这一轮的最终
|
|
493
|
+
* 答复与上面流式显示的内容不一致」,而不是让用户以为屏上那段就是最终答复。不接本臂 = 用户
|
|
494
|
+
* 看不到这条披露(不是报错),但**绝不许**把它渲成「已完成」的正面确认。
|
|
495
|
+
* 🔴 两个数是**长度**不是内容:本臂不复述任何一份正文(屏上那份宿主自己有,终帧那份在 result 帧上),
|
|
496
|
+
* 复述一遍只会给渲染端第三个版本去挑。
|
|
497
|
+
*/
|
|
498
|
+
export interface ResultTextDivergedChromeEvent {
|
|
499
|
+
kind: 'result_text_diverged';
|
|
500
|
+
laneProof: LaneProof;
|
|
501
|
+
/** 本 turn 已经**提交上屏**的 assistant 正文长度(UTF-16 单元)。 */
|
|
502
|
+
committedLength: number;
|
|
503
|
+
/** 终帧 `result` 那份正文的长度(UTF-16 单元)。 */
|
|
504
|
+
resultLength: number;
|
|
505
|
+
}
|
|
483
506
|
/**
|
|
484
507
|
* {@link ChromeEvent} 的 `engine_notice` 臂(#310 / #318 件①,server ≥7.36;契约 = server
|
|
485
508
|
* `ASSISTANT-WIRE-CONTRACT` 附录 D)——「**引擎想让这条会话的人知道一件事**」。
|
package/dist/seam.js
CHANGED
|
@@ -56,6 +56,13 @@ const CHROME_ARM_TABLE = {
|
|
|
56
56
|
'mcp 的空数组是「一台都没申报」这句正面事实、**不是**缺席(判在场写 mcp !== undefined,' +
|
|
57
57
|
'别写 mcp?.length),errorCode 按开集分支必带 default;durable 重放按 run/leg 幂等',
|
|
58
58
|
},
|
|
59
|
+
result_text_diverged: {
|
|
60
|
+
required: false,
|
|
61
|
+
duty: '可选:终帧正文与已上屏正文**互相都不是对方前缀**时的诚实披露。🔴 处置是把这一拍的转录行**改口**' +
|
|
62
|
+
'(「最终答复与上面流式显示的内容不一致」),不是补渲一段 —— 两份对不上时谁算数只有引擎说得清,' +
|
|
63
|
+
'把终帧那份接在屏上那段后面会拼出一段谁都没说过的话。不接 = 这条披露看不见(不是报错),' +
|
|
64
|
+
'但绝不许把它渲成「已完成」的正面确认;两个位是长度不是内容,别拿它们当正文来源',
|
|
65
|
+
},
|
|
59
66
|
text_segment_end: {
|
|
60
67
|
required: false,
|
|
61
68
|
duty: '可选:引擎明报的 assistant 散文段边界(#323/core #447)。🔴 content 是对账/定界用的权威全文,拿它再渲一行 = 同一段上屏两遍;缺席只表示「没报」,绝不等于「段没结束」——要退回自家启发式必须按整条流判、不按单帧判',
|
package/dist/seatContract.d.ts
CHANGED
|
@@ -512,6 +512,34 @@ export interface SendMessageRequest {
|
|
|
512
512
|
* shell's lane defaults it ON (2026-07-26 flip), so a seat that cannot send it is not "defaulting
|
|
513
513
|
* to the same thing" — it is letting the ENGINE's default decide, which is a different decision. */
|
|
514
514
|
retainBackgroundProcesses?: boolean;
|
|
515
|
+
/**
|
|
516
|
+
* 这一轮把**子代的正文帧**转发到父流上 → `TaskRequest.forwardSubagentEvents`。缺席 = 不开
|
|
517
|
+
* (父流只有进度帧,没有子代的 text/reasoning/tool 帧)。
|
|
518
|
+
*
|
|
519
|
+
* 🔴 **能力位说的是「这个键被接受」,不是「它已经开着」**:引擎的 `capabilities.forwardSubagentEvents`
|
|
520
|
+
* 是一个恒真的**受理位**,把它读成「转发已生效」的消费方会接出一个**永远空着**的子代转录面,
|
|
521
|
+
* 而能力面一直在回答「是」。⇒ 想要子代正文,**每一轮都得自己带这个键**。
|
|
522
|
+
* 🔴 结构性缺席故事与 `reasoningEffort` 同款:闭集载体就是这条 lane 本身,不在集里 = 「这一轮要不要
|
|
523
|
+
* 看子代正文」这个决定在座位 lane 上**没有位置可放**。座位端此前只能整条放弃子代转录面
|
|
524
|
+
* (或绕开闭集从逃生口塞一个没登记的键上 wire —— 那是本包整张契约存在的理由所要消灭的形)。
|
|
525
|
+
*/
|
|
526
|
+
forwardSubagentEvents?: boolean;
|
|
527
|
+
/**
|
|
528
|
+
* 这一轮**保留已结束的子代会话**以便日后续跑 → `TaskRequest.retainSubagentSessions`。
|
|
529
|
+
* `true` = 按引擎默认保留;对象形调保留**窗口**(`ttlMs`)与**条数**(`max`)。缺席 = 不保留。
|
|
530
|
+
*
|
|
531
|
+
* 🔴 **过大的请求会被引擎悄悄收紧、而不是拒绝**(server 侧 clamp:窗口上限 24 小时、条数上限 64)。
|
|
532
|
+
* ⇒ 渲染端**不许**把自己请求的值当成生效值回显 —— 那是替引擎说了一句它没说的话;要显示生效值
|
|
533
|
+
* 就去读引擎那一面,读不到就诚实留白。
|
|
534
|
+
* 🔴 它是「子代续跑」那条路的**另一半**:续跑能力位可以是真、路由可以在,调用照样会被拒 ——
|
|
535
|
+
* 因为保留是**逐 run**的、且默认关。只看能力位就渲一个「继续这个子代」的入口 = 假 affordance。
|
|
536
|
+
* 🔴 结构性缺席故事同上:此前座位 lane 没有这一位,于是「这条会话要不要留子代」只能由部署默认决定,
|
|
537
|
+
* 而那与「端替用户做了这个决定」不是一回事。
|
|
538
|
+
*/
|
|
539
|
+
retainSubagentSessions?: boolean | {
|
|
540
|
+
ttlMs?: number;
|
|
541
|
+
max?: number;
|
|
542
|
+
};
|
|
515
543
|
/** Per-turn settings sub-object → `TaskRequest.settings`. Closed shape (`SeatTurnSettings`);
|
|
516
544
|
* today's only member is the `ultracode` preset boolean. */
|
|
517
545
|
settings?: SeatTurnSettings;
|
|
@@ -739,7 +767,62 @@ export declare const SEAT_EVENT_METHOD_NAMES: readonly ["onEvent", "onToolPermis
|
|
|
739
767
|
export declare const LOCAL_SESSION_EVENT_KEYS: readonly ["type", "sessionId", "message", "messages", "error", "errorCategory", "code", "request", "data", "sources", "permissionMode", "session", "userMessageUuid", "hasPreClearSession", "requestId", "outcome", "chrome"];
|
|
740
768
|
export declare const TOOL_PERMISSION_REQUEST_KEYS: readonly ["requestId", "sessionId", "toolName", "toolCallId", "input", "message", "toctouBinding", "createdAtMs", "governanceForced", "delegation"];
|
|
741
769
|
export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked"];
|
|
742
|
-
export declare const SEND_MESSAGE_REQUEST_KEYS: readonly ["sessionId", "text", "model", "userMessageUuid", "permissionMode", "attachmentIds", "reasoningEffort", "retainBackgroundProcesses", "settings", "scenario"];
|
|
770
|
+
export declare const SEND_MESSAGE_REQUEST_KEYS: readonly ["sessionId", "text", "model", "userMessageUuid", "permissionMode", "attachmentIds", "reasoningEffort", "retainBackgroundProcesses", "forwardSubagentEvents", "retainSubagentSessions", "settings", "scenario"];
|
|
771
|
+
/**
|
|
772
|
+
* **座位键 → 引擎请求位**的出身表(0.62.0;`SEND_MESSAGE_REQUEST_KEYS` 的逐键出身登记)。
|
|
773
|
+
*
|
|
774
|
+
* 为什么要有它:座位 lane 的载荷是一个**闭集接口**,而它的绝大多数位最终是要**原样落到引擎请求
|
|
775
|
+
* 体上**的。这两件事此前没有任何东西把它们接起来 —— 一个座位位可以叫一个引擎请求体上**根本不存在**
|
|
776
|
+
* 的名字(端照着它写、上了 wire 被引擎整键忽略,面上是「开关点了没反应」这类静默缺席),而引擎请求
|
|
777
|
+
* 体新长出一个位时,也没有任何信号说「座位 lane 还没有它的席位」。本表把每个座位键的出身写成**数据**:
|
|
778
|
+
* · `taskRequestKey` —— 这个位**原样转投**引擎请求体的同名/异名位。常驻门拿它对**装在盘上的**
|
|
779
|
+
* sdk 型面逐名核(型直读,不手抄):名字不在引擎请求体上 ⇒ 当天红。
|
|
780
|
+
* · `seatOnly` —— 这个位**不上 wire**,只活在座位 lane 上;字符串写清它是什么、为什么不转投。
|
|
781
|
+
*
|
|
782
|
+
* 🔴 `sessionId` 是**同名不同义**的典型:座位这一位是**座位会话句柄**(渲染端持有的本地 id),而引擎
|
|
783
|
+
* 请求体上那个同名位是**引擎会话 id**(座位记录里另有 `engineSessionId` 承载)。两者恰好同名,
|
|
784
|
+
* 把它登记成「转投」会让下一个人以为直接透传就行 —— 那是把本地句柄当引擎会话送上 wire。
|
|
785
|
+
* 🔴 `text` 是**异名转投**:座位说 `text`,引擎请求体那一位叫 `objective`。异名不是问题,**没记账**
|
|
786
|
+
* 才是;本表就是那本账。
|
|
787
|
+
*/
|
|
788
|
+
export declare const SEAT_SEND_MESSAGE_KEY_ORIGINS: {
|
|
789
|
+
readonly sessionId: {
|
|
790
|
+
readonly seatOnly: "座位会话句柄(渲染端持有的本地 id);引擎会话 id 由座位记录的 `engineSessionId` 承载 —— 与引擎请求体上的同名位**同名不同义**,绝不直传";
|
|
791
|
+
};
|
|
792
|
+
readonly text: {
|
|
793
|
+
readonly taskRequestKey: "objective";
|
|
794
|
+
};
|
|
795
|
+
readonly model: {
|
|
796
|
+
readonly taskRequestKey: "model";
|
|
797
|
+
};
|
|
798
|
+
readonly userMessageUuid: {
|
|
799
|
+
readonly seatOnly: "用户消息的回声键(对标产品形态:客户端自铸、原样回显到转录上),不是引擎请求体上的位";
|
|
800
|
+
};
|
|
801
|
+
readonly permissionMode: {
|
|
802
|
+
readonly taskRequestKey: "permissionMode";
|
|
803
|
+
};
|
|
804
|
+
readonly attachmentIds: {
|
|
805
|
+
readonly taskRequestKey: "attachmentIds";
|
|
806
|
+
};
|
|
807
|
+
readonly reasoningEffort: {
|
|
808
|
+
readonly taskRequestKey: "reasoningEffort";
|
|
809
|
+
};
|
|
810
|
+
readonly retainBackgroundProcesses: {
|
|
811
|
+
readonly taskRequestKey: "retainBackgroundProcesses";
|
|
812
|
+
};
|
|
813
|
+
readonly forwardSubagentEvents: {
|
|
814
|
+
readonly taskRequestKey: "forwardSubagentEvents";
|
|
815
|
+
};
|
|
816
|
+
readonly retainSubagentSessions: {
|
|
817
|
+
readonly taskRequestKey: "retainSubagentSessions";
|
|
818
|
+
};
|
|
819
|
+
readonly settings: {
|
|
820
|
+
readonly taskRequestKey: "settings";
|
|
821
|
+
};
|
|
822
|
+
readonly scenario: {
|
|
823
|
+
readonly taskRequestKey: "scenario";
|
|
824
|
+
};
|
|
825
|
+
};
|
|
743
826
|
export declare const TOOL_PERMISSION_DECISION_KEYS: readonly ["sessionId", "requestId", "decision", "answer", "updatedInput", "remember"];
|
|
744
827
|
export declare const START_SESSION_OPTION_KEYS: readonly ["model", "title", "cwd", "excludeTools"];
|
|
745
828
|
/**
|
package/dist/seatContract.js
CHANGED
|
@@ -753,10 +753,45 @@ export const SEND_MESSAGE_REQUEST_KEYS = [
|
|
|
753
753
|
// 载体本身,不在集里 = 那个决定在这条 lane 上根本没有位置可放。
|
|
754
754
|
"reasoningEffort",
|
|
755
755
|
"retainBackgroundProcesses",
|
|
756
|
+
// 0.62.0(子代面两位补载体):同上结构性缺席形 —— 「这一轮要不要看子代正文 / 要不要留子代会话」
|
|
757
|
+
// 此前在座位 lane 上没有位置可放。两位都**原样转投**同名引擎请求位(出身表 + 常驻门对账)。
|
|
758
|
+
"forwardSubagentEvents",
|
|
759
|
+
"retainSubagentSessions",
|
|
756
760
|
"settings",
|
|
757
761
|
// [C131] 补载体(2026-08-08):scenario 拨盘的座位 lane 席位(同上结构性缺席形)。
|
|
758
762
|
"scenario",
|
|
759
763
|
];
|
|
764
|
+
/**
|
|
765
|
+
* **座位键 → 引擎请求位**的出身表(0.62.0;`SEND_MESSAGE_REQUEST_KEYS` 的逐键出身登记)。
|
|
766
|
+
*
|
|
767
|
+
* 为什么要有它:座位 lane 的载荷是一个**闭集接口**,而它的绝大多数位最终是要**原样落到引擎请求
|
|
768
|
+
* 体上**的。这两件事此前没有任何东西把它们接起来 —— 一个座位位可以叫一个引擎请求体上**根本不存在**
|
|
769
|
+
* 的名字(端照着它写、上了 wire 被引擎整键忽略,面上是「开关点了没反应」这类静默缺席),而引擎请求
|
|
770
|
+
* 体新长出一个位时,也没有任何信号说「座位 lane 还没有它的席位」。本表把每个座位键的出身写成**数据**:
|
|
771
|
+
* · `taskRequestKey` —— 这个位**原样转投**引擎请求体的同名/异名位。常驻门拿它对**装在盘上的**
|
|
772
|
+
* sdk 型面逐名核(型直读,不手抄):名字不在引擎请求体上 ⇒ 当天红。
|
|
773
|
+
* · `seatOnly` —— 这个位**不上 wire**,只活在座位 lane 上;字符串写清它是什么、为什么不转投。
|
|
774
|
+
*
|
|
775
|
+
* 🔴 `sessionId` 是**同名不同义**的典型:座位这一位是**座位会话句柄**(渲染端持有的本地 id),而引擎
|
|
776
|
+
* 请求体上那个同名位是**引擎会话 id**(座位记录里另有 `engineSessionId` 承载)。两者恰好同名,
|
|
777
|
+
* 把它登记成「转投」会让下一个人以为直接透传就行 —— 那是把本地句柄当引擎会话送上 wire。
|
|
778
|
+
* 🔴 `text` 是**异名转投**:座位说 `text`,引擎请求体那一位叫 `objective`。异名不是问题,**没记账**
|
|
779
|
+
* 才是;本表就是那本账。
|
|
780
|
+
*/
|
|
781
|
+
export const SEAT_SEND_MESSAGE_KEY_ORIGINS = {
|
|
782
|
+
sessionId: { seatOnly: "座位会话句柄(渲染端持有的本地 id);引擎会话 id 由座位记录的 `engineSessionId` 承载 —— 与引擎请求体上的同名位**同名不同义**,绝不直传" },
|
|
783
|
+
text: { taskRequestKey: "objective" },
|
|
784
|
+
model: { taskRequestKey: "model" },
|
|
785
|
+
userMessageUuid: { seatOnly: "用户消息的回声键(对标产品形态:客户端自铸、原样回显到转录上),不是引擎请求体上的位" },
|
|
786
|
+
permissionMode: { taskRequestKey: "permissionMode" },
|
|
787
|
+
attachmentIds: { taskRequestKey: "attachmentIds" },
|
|
788
|
+
reasoningEffort: { taskRequestKey: "reasoningEffort" },
|
|
789
|
+
retainBackgroundProcesses: { taskRequestKey: "retainBackgroundProcesses" },
|
|
790
|
+
forwardSubagentEvents: { taskRequestKey: "forwardSubagentEvents" },
|
|
791
|
+
retainSubagentSessions: { taskRequestKey: "retainSubagentSessions" },
|
|
792
|
+
settings: { taskRequestKey: "settings" },
|
|
793
|
+
scenario: { taskRequestKey: "scenario" },
|
|
794
|
+
};
|
|
760
795
|
export const TOOL_PERMISSION_DECISION_KEYS = [
|
|
761
796
|
"sessionId",
|
|
762
797
|
"requestId",
|
|
@@ -957,6 +992,8 @@ export const SEAT_VALIDATOR_COVERAGE_EXEMPT = [
|
|
|
957
992
|
const _seatContractPins = [
|
|
958
993
|
true, true, true, true, true, true, true, true, true,
|
|
959
994
|
true, true, true, true, true, true, true, true,
|
|
995
|
+
// 0.62.0:出身表 ↔ 载荷键集双向
|
|
996
|
+
true, true,
|
|
960
997
|
true, true,
|
|
961
998
|
true, true, true, true,
|
|
962
999
|
true, true, true, true,
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
*
|
|
39
39
|
* ── 🔴 UNTRUSTED-for-display ──────────────────────────────────────────────────────────────
|
|
40
40
|
* `isolation` 是从**数据库服务器**变量里回读的串(`@@transaction_isolation` 一族),不是引擎铸的
|
|
41
|
-
*
|
|
42
|
-
* ({@link
|
|
41
|
+
* 闭词;三座一律只渲染、绝不参与任何判定,呈现前过本包的单行消毒 + 封长单源
|
|
42
|
+
* ({@link capForDisplay}:先转义、后按转义结果封长)。
|
|
43
43
|
*/
|
|
44
44
|
/**
|
|
45
45
|
* 三座俱全时的读数(server `SqlEngineCapability` 逐字同形)。
|