@sema-agent/client-core 0.59.2 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +245 -0
- package/README.md +4 -2
- package/dist/adapt/toolCards.d.ts +2 -1
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +24 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +42 -70
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +27 -22
- package/dist/adapter/downstream/terminalToSdkResult.js +47 -30
- package/dist/adapter/runStream.js +27 -11
- package/dist/engineCapsCache.d.ts +27 -0
- package/dist/engineCapsCache.js +29 -0
- package/dist/engineErrorCodes.d.ts +12 -0
- package/dist/engineErrorCodes.js +13 -0
- package/dist/gateOutcome.d.ts +157 -0
- package/dist/gateOutcome.js +181 -0
- package/dist/headlessReconnectWire.js +9 -2
- package/dist/hitl/askParkRowRouting.d.ts +2 -0
- package/dist/hitl/askParkRowRouting.js +17 -2
- package/dist/hitl/frameRouter.d.ts +2 -2
- package/dist/hitl/frameRouter.js +49 -35
- package/dist/hitl/hitlBridge.d.ts +49 -2
- package/dist/hitl/hitlBridge.js +22 -1
- package/dist/hitl/parkResolver.d.ts +3 -1
- package/dist/hitl/parkResolver.js +15 -5
- package/dist/hitl/planReviewWire.d.ts +8 -1
- package/dist/hitl/planReviewWire.js +18 -7
- package/dist/hitl/toolApprovalWire.d.ts +37 -3
- package/dist/hitl/toolApprovalWire.js +36 -9
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -0
- package/dist/printToolResultFrame.d.ts +34 -49
- package/dist/readFacePosture.d.ts +53 -0
- package/dist/readFacePosture.js +120 -0
- package/dist/resumeRefusalCopy.d.ts +12 -4
- package/dist/resumeRefusalCopy.js +15 -5
- package/dist/runTerminal.d.ts +194 -0
- package/dist/runTerminal.js +189 -0
- package/dist/sqlEngineCapability.d.ts +11 -1
- package/dist/sqlEngineCapability.js +13 -1
- package/dist/writeProtectionCapability.d.ts +142 -0
- package/dist/writeProtectionCapability.js +224 -0
- package/docs/INTEGRATION-CLIENTS.md +433 -11
- package/package.json +3 -3
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { stamp } from '../types.js';
|
|
2
|
+
// 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局读数的**单一读器**(两代字节 → 一个带标因由)。
|
|
3
|
+
import { isReviewPark, readRunTerminal, runTerminalCode, runTerminalGateToolName, } from '../../runTerminal.js';
|
|
2
4
|
import { toCcModelUsage } from './turnUsageToModelUsage.js';
|
|
3
5
|
// G1 去字面化(2026-08-04):到限/结构化输出/rewind 三族的码字面收编进单一真源,本文件只 import。
|
|
4
6
|
// 开集纪律不变——下面三个集合仍是**识别表**,`subtypeForErrorCode` 的 default 臂才是开集的兑现处。
|
|
@@ -273,11 +275,19 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
273
275
|
// looked like a widening but wasn't one. `Omit` the narrow key first so the added wider
|
|
274
276
|
// `status?: string` actually applies — that is also what keeps the 未知终态 defensive arm below
|
|
275
277
|
// expressible at all.
|
|
276
|
-
|
|
278
|
+
// 🔴 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局分流的判据从**八个平面键**换成**一条带标因由**
|
|
279
|
+
// (`result.terminal`)。读法单源在 `runTerminal.readRunTerminal` —— 本文件一个 `status` 字面
|
|
280
|
+
// 都不再比较(平面形的两个来源、以及「不认识的终态词绝不落成功」那条不变量,全在读器里)。
|
|
281
|
+
// `null` = 这份载体上连终态词都没有(mock 车道 / 不带 status 的旧合成帧)⇒ 维持成功臂,
|
|
282
|
+
// 与形变前逐字节同行为。
|
|
283
|
+
const terminal = readRunTerminal(rRaw);
|
|
277
284
|
const durationMs = elapsedMs(ctx);
|
|
278
285
|
// [909]B1 — errorCode 透传(additive seam 字段;CC schema 无此键,与 success 臂 `model` 同款超集
|
|
279
286
|
// 姿势):引擎值原样,绝不铸造。所有 error 臂共用(`errorResult` 信封负责 stamp)。
|
|
280
|
-
|
|
287
|
+
// 🔴 因由形下「码属于失败」是**形状**:paused / blocked 上引擎写不出码 ⇒ 这两臂的 `errorCode`
|
|
288
|
+
// 从 0.60.0 起恒缺席(此前 `review.pending` 骑在 park 帧的平面 errorCode 上)。扁平形回放的
|
|
289
|
+
// 历史行仍带得动它(读器在那三臂上保留 `code`),所以盘上真有的事实不丢。
|
|
290
|
+
const errorCode = runTerminalCode(terminal);
|
|
281
291
|
// TaskResult 五键最小消费批([1543] SDK-M,2026-07-23):additive seam 字段——
|
|
282
292
|
// · degraded = 模型降级链实录,**各终态恒 additive 附上**(见 `degradedOf` 头注 / REF-CC-056);
|
|
283
293
|
// · structuredOutput = outputSchema 任务的结构化产出(success 臂);
|
|
@@ -286,32 +296,34 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
286
296
|
const degraded = degradedOf(r);
|
|
287
297
|
/** 四个 error 臂共享的固定位(信封其余 13 位见 `errorResult`)。 */
|
|
288
298
|
const errorBase = { durationMs, stats, model: r.model, errorCode, degraded };
|
|
289
|
-
if (
|
|
299
|
+
if (terminal?.kind === 'failed') {
|
|
290
300
|
// [909]B1 — failed 臂 subtype 语义化(见文件头对表);core 5.8.0([2489])起到限码全部改名,
|
|
291
301
|
// 映射本身已收进单点 `subtypeForErrorCode`(退役批后只认新码)。
|
|
292
302
|
// 5.8.0 的「截断」族到限(token/墙钟):subtype 只能落诚实兜底词,但这两种停因**可能已产出正文**
|
|
293
303
|
// —— [2489] 对 limits.max_tokens_exceeded 明记「result 带已产出正文」,墙钟停是既有写出窗语义,
|
|
294
304
|
// 两族共用 `cutoffParts`(salvage + 零抢救诚实尾注)。
|
|
295
|
-
const
|
|
296
|
-
|
|
305
|
+
const failedCode = terminal.code;
|
|
306
|
+
const failedMessage = terminal.message;
|
|
307
|
+
const failedSubtype = subtypeForErrorCode(failedCode);
|
|
308
|
+
if (failedCode !== undefined && CUTOFF_EXCEEDED_CODES.has(failedCode)) {
|
|
297
309
|
return errorResult(ctx, {
|
|
298
310
|
...errorBase,
|
|
299
311
|
subtype: failedSubtype,
|
|
300
|
-
...cutoffParts(r,
|
|
312
|
+
...cutoffParts(r, failedMessage, failedCode === LIMITS_MAX_TOKENS_EXCEEDED
|
|
301
313
|
? 'run exhausted its token budget'
|
|
302
|
-
: 'run hit its wall-clock limit',
|
|
314
|
+
: 'run hit its wall-clock limit', failedCode),
|
|
303
315
|
});
|
|
304
316
|
}
|
|
305
317
|
return errorResult(ctx, {
|
|
306
318
|
...errorBase,
|
|
307
319
|
subtype: failedSubtype,
|
|
308
320
|
// rewind 族错误码透传(server 1.192 [838]③ 实测:stream 腿把 resume_at.before_* /
|
|
309
|
-
// rewind_snapshot.unresolvable
|
|
310
|
-
//
|
|
321
|
+
// rewind_snapshot.unresolvable 骑在失败因由的码上)—— 与 failedToSdkResult 的同款 append
|
|
322
|
+
// 对齐,message 后附 code 便于对账。
|
|
311
323
|
errors: [
|
|
312
|
-
isRewindFamilyCode(
|
|
313
|
-
? `${
|
|
314
|
-
: (
|
|
324
|
+
isRewindFamilyCode(failedCode) && failedMessage
|
|
325
|
+
? `${failedMessage} (${failedCode})`
|
|
326
|
+
: (failedMessage ?? 'run failed'),
|
|
315
327
|
],
|
|
316
328
|
});
|
|
317
329
|
}
|
|
@@ -324,13 +336,13 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
324
336
|
// [909]B1 — blocked 终态:agent 自报无法推进(assemble-result.js:99,只带 blockedReason 无
|
|
325
337
|
// errorCode)。此前落 success 臂(TB db-wal-recovery__uuH5cAg 实证:引擎终态 blocked、帧却
|
|
326
338
|
// subtype:success)。≠空成功 → SDKResultError,blockedReason 如实进 errors[]。
|
|
327
|
-
if (
|
|
328
|
-
const blockedReason =
|
|
339
|
+
if (terminal?.kind === 'blocked') {
|
|
340
|
+
const blockedReason = terminal.reason;
|
|
329
341
|
return errorResult(ctx, {
|
|
330
342
|
...errorBase,
|
|
331
343
|
subtype: 'error_during_execution',
|
|
332
344
|
errors: [
|
|
333
|
-
|
|
345
|
+
blockedReason !== undefined
|
|
334
346
|
? `Run blocked: ${blockedReason}`
|
|
335
347
|
: 'Run blocked (the agent reported it cannot proceed)',
|
|
336
348
|
],
|
|
@@ -350,20 +362,20 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
350
362
|
// CC 对照:CC 2.1.207 AskUserQuestion.isEnabled 在非交互且无 SDK permissionPromptTool 时
|
|
351
363
|
// return false = 模型源头看不到该工具;core [884] 自领 1.296 加同款非交互闸(roster 源头不进)。
|
|
352
364
|
// 本分流是闸到货后的防御纵深:即便引擎吐了 suspended,壳也不许洗成空成功。
|
|
353
|
-
if (
|
|
365
|
+
if (terminal?.kind === 'paused') {
|
|
354
366
|
const parked = r;
|
|
355
367
|
const runId = typeof parked.taskId === 'string' && parked.taskId.length > 0 ? parked.taskId : undefined;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
368
|
+
// 🔴 0.60.0:两句措辞的判据从退役的 `status` 五词换成**门种**(因由形下 park 只带 gate)——
|
|
369
|
+
// 单源 `runTerminal.isReviewPark`(词表 ⊇ 重开臂路由表,包含关系由常驻门钉)。
|
|
370
|
+
const gateTool = runTerminalGateToolName(terminal);
|
|
371
|
+
const review = isReviewPark(terminal);
|
|
359
372
|
const sid = typeof ctx.sessionId === 'string' && ctx.sessionId.length > 0 ? ctx.sessionId : undefined;
|
|
360
|
-
const awaiting =
|
|
361
|
-
|
|
362
|
-
: `an interactive answer${gateTool ? ` (${gateTool})` : ''}`;
|
|
363
|
-
const wayOut = rr.status === 'needs_review'
|
|
373
|
+
const awaiting = review ? 'a plan review' : `an interactive answer${gateTool ? ` (${gateTool})` : ''}`;
|
|
374
|
+
const wayOut = review
|
|
364
375
|
? `Review the plan in the interactive REPL${sid ? ` (sema --resume ${sid})` : ''}.`
|
|
365
376
|
: `Answer it in the interactive REPL${sid ? ` (sema --resume ${sid})` : ''}, or decide the pending approval via the engine approvals API${sid ? ` (POST /v1/approvals/${sid}/decide)` : ''}.`;
|
|
366
|
-
// [909]B1 — needs_review
|
|
377
|
+
// [909]B1 — 平面形回放的 needs_review 行仍带 review.pending;因由形下 park **写不出**码
|
|
378
|
+
// (见上方 errorCode 头注),两代都由信封原样透传。
|
|
367
379
|
return errorResult(ctx, {
|
|
368
380
|
...errorBase,
|
|
369
381
|
subtype: 'error_during_execution',
|
|
@@ -372,16 +384,21 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
372
384
|
],
|
|
373
385
|
});
|
|
374
386
|
}
|
|
375
|
-
// [909]B1 —
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
|
|
387
|
+
// [909]B1 — 防御臂:四臂之外的未知终态词(core 未来加因由臂 / 未知 wire 变形)绝不洗成空成功
|
|
388
|
+
// —— error_during_execution + errors[] 如实报出那个词的字面。读器把两代字节的这一格都归到
|
|
389
|
+
// `unknown` 臂(因由形取 `terminal.kind`,扁平形取 `status`),所以本臂对两代同样有效。
|
|
390
|
+
// 🔴 **终态词整个缺席**(mock 车道 / 旧合成帧;读器返 `null`)维持 success 臂,零行为变化。
|
|
391
|
+
if (terminal?.kind === 'unknown') {
|
|
379
392
|
return errorResult(ctx, {
|
|
380
393
|
...errorBase,
|
|
381
394
|
subtype: 'error_during_execution',
|
|
382
395
|
errors: [
|
|
383
|
-
|
|
384
|
-
|
|
396
|
+
terminal.message ??
|
|
397
|
+
(terminal.word !== undefined
|
|
398
|
+
? `run ended with terminal cause "${terminal.word}" (not a success; this CLI predates that terminal word)`
|
|
399
|
+
: // 🔴 因由座在场却整个读不出(坏形 / 混合载体)—— 说的是「读不出」,不是「没有终局」,
|
|
400
|
+
// 更不是成功。诚实缺席优先于编一个词([honest-absence-not-fabricated-zero])。
|
|
401
|
+
'run ended with a terminal cause this client could not read (not a success; the engine sent a malformed terminal record)'),
|
|
385
402
|
],
|
|
386
403
|
});
|
|
387
404
|
}
|
|
@@ -8,6 +8,8 @@ import { coerceOutput, publishSubagentContentEvent } from '../subagentContentSto
|
|
|
8
8
|
* G1 去字面化(2026-08-04):字面收编进 `engineErrorCodes.ts` 单一真源,本文件只 import。
|
|
9
9
|
*/
|
|
10
10
|
import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isLimitsExceededCode } from '../engineErrorCodes.js';
|
|
11
|
+
// 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局读数的单一读器(纯叶子:零 Node、零宿主端口)。
|
|
12
|
+
import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
|
|
11
13
|
/** 本文件发的 chrome 事件全在 leader lane(子代内容在上面就被 divert 走了)。 */
|
|
12
14
|
const MAIN = { lane: 'main' };
|
|
13
15
|
/**
|
|
@@ -378,7 +380,10 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
378
380
|
// (interactive REPL 不渲染 result 帧)= 上游 4xx/欠费/超限对用户完全静默。这里
|
|
379
381
|
// 额外合成一条 assistant 错误消息(CC 的 isApiErrorMessage 红字渲染线现成),
|
|
380
382
|
// 用户永远能看到 turn 为什么失败。
|
|
381
|
-
|
|
383
|
+
// 🔴 0.60.0(engine ≥7.64.0 / sdk 8.4.0):done 帧的终局读数换成带标因由,读法单源
|
|
384
|
+
// `runTerminal.readRunTerminal`(它同时认扁平形的**三个**来源:读盘回放的历史行 /
|
|
385
|
+
// server 自己的 409 拒绝信封 / 同步提交腿的 park 体)。本文件从此一个平面键都不直读。
|
|
386
|
+
const doneTerminal = ev.type === 'done' ? readRunTerminal(ev.result) : null;
|
|
382
387
|
// [909]B1 — failed/blocked 终态同享可见性行(此前只 status:'failed' 有行,墙钟停/blocked
|
|
383
388
|
// 在 REPL 面完全静默)。park(suspended/needs_review)不进此行:有各自 overlay/文案线,
|
|
384
389
|
// 非 provider 故障([884]A1 断言零 assistant 行)。
|
|
@@ -390,11 +395,11 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
390
395
|
// 上 wire,该腿随之删除,不留静默缺口。
|
|
391
396
|
const errText = ev.type === 'failed'
|
|
392
397
|
? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
|
|
393
|
-
:
|
|
394
|
-
? (
|
|
395
|
-
:
|
|
396
|
-
? (
|
|
397
|
-
? `Run blocked: ${
|
|
398
|
+
: doneTerminal?.kind === 'failed'
|
|
399
|
+
? (doneTerminal.message ?? doneTerminal.code ?? 'run failed')
|
|
400
|
+
: doneTerminal?.kind === 'blocked'
|
|
401
|
+
? (doneTerminal.reason
|
|
402
|
+
? `Run blocked: ${doneTerminal.reason}`
|
|
398
403
|
: 'Run blocked (the agent reported it cannot proceed)')
|
|
399
404
|
: null;
|
|
400
405
|
if (errText) {
|
|
@@ -407,12 +412,14 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
407
412
|
// suspended/needs_review 就地终态化 ⇒ claim 释放),所以文案指的就是那个端点;
|
|
408
413
|
// 自愈分诊腿(重开卡把决定权还给用户;cancel+重发臂已退役)在 `adapter/activeRunSelfHeal.ts`
|
|
409
414
|
// (A-028.1 上收,宿主经 deps 注入重开口),本层只负责:识别 + 说真话。
|
|
410
|
-
|
|
415
|
+
// `activeTaskId` 是**拒绝信封自己的**位(server 的 409 帧,不是引擎结果)⇒ 照旧从帧体直读,
|
|
416
|
+
// 不经因由读器(因由里没有这一位,也不该有)。
|
|
417
|
+
const activeTaskId = (ev.type === 'done' ? ev.result?.activeTaskId : undefined) ??
|
|
411
418
|
(ev.type === 'failed' ? ev.activeTaskId : undefined);
|
|
412
419
|
// REF-CC-054:判别**结构位两腿**(errorCode → activeTaskId;文案兜底腿 2026-08-03 已退役),
|
|
413
420
|
// 判别体见文件上方 `classifyActiveRunBusy`。
|
|
414
421
|
const busy = classifyActiveRunBusy({
|
|
415
|
-
errorCode: ev.type === 'failed' ? ev.errorCode :
|
|
422
|
+
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
416
423
|
activeTaskId,
|
|
417
424
|
});
|
|
418
425
|
// 件14 rev2 (2026-07-14): provider max_tokens cap rejection (e.g. glm-5.2 preset 131072 >
|
|
@@ -442,9 +449,12 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
442
449
|
// 不是模型说的话」),不是「这是 provider API 错误」的断言 —— 可见性过滤
|
|
443
450
|
// (`Messages.filterForBriefTool`)、-p 契约投影、tick 熔断都读它,撤掉 = 这一行在
|
|
444
451
|
// brief 模式里静默消失。改的是**身份文案**,不是行类。
|
|
452
|
+
// 🔴 `isGovernanceTerminal` 的入参仍是**词面**(它是一个纯谓词,公面签名不动)——
|
|
453
|
+
// 因由形下「被治理挡下」这件事由 `kind === 'blocked'` 承载,喂给它那一个词即可;
|
|
454
|
+
// 其余臂喂 `undefined`(码那一半照旧走 errorCode 腿)。
|
|
445
455
|
const governance = isGovernanceTerminal({
|
|
446
|
-
status: ev.type === 'failed' ? undefined :
|
|
447
|
-
errorCode: ev.type === 'failed' ? ev.errorCode :
|
|
456
|
+
status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
|
|
457
|
+
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
448
458
|
});
|
|
449
459
|
const busyHandle = busy.handle;
|
|
450
460
|
const rowText = busy.busy
|
|
@@ -475,7 +485,13 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
475
485
|
// B3 切边:此前是 `await import('../../sema/planReviewWire.js')` —— **整棵 CC 树进闭包的
|
|
476
486
|
// 那一行**(设计稿 §5.2 根因链)。改成 chrome 事件;`await` 保留(cli 原本就在终帧**之前**
|
|
477
487
|
// 同步等 arm 完成,卡片先于完成行出现),sink 失败 fail-soft 照常投影终帧。
|
|
478
|
-
|
|
488
|
+
// 🔴 0.60.0:判据换成 `isReviewPark`,它**两个来源合读**(因由形的门种 ∪ 扁平形的 status 词)。
|
|
489
|
+
// ⚠️ **扁平那一半是必须的,不是残留**(server 回帖确认):同步提交腿命中 park 时,server 自造
|
|
490
|
+
// 的 200 体是 `{taskId, sessionId, status:"suspended"|"needs_review"}` —— **既没有退役的八个
|
|
491
|
+
// 平面键、也没有 `terminal`、连门都不带**,它不是 `TaskResult` 而是独立于它的另一只形。
|
|
492
|
+
// 在那一形上 `status:"needs_review"` 是「这是一次复核」的**唯一**信号;只读门种会让整条
|
|
493
|
+
// 同步 park 腿的 plan-review 卡不弹。因由形那一半覆盖 done 帧的引擎结果,两半各管一条腿。
|
|
494
|
+
if (ev.type === 'done' && isReviewPark(doneTerminal) && ctx.emitChrome) {
|
|
479
495
|
try {
|
|
480
496
|
await ctx.emitChrome({ kind: 'plan_review_park', laneProof: MAIN, result: ev.result });
|
|
481
497
|
}
|
|
@@ -25,6 +25,33 @@
|
|
|
25
25
|
* restartEngine)在重启后调用 —— 缓存自己无从分辨「同一个 baseUrl 后面还是不是同一个引擎」,
|
|
26
26
|
* 猜(TTL / 每次构造清)只会把一个确定事实换成一个定时器。
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* 该 base 的**当代号**(公面读口;0.60.0)。
|
|
30
|
+
*
|
|
31
|
+
* ── 它解决的是哪一件事(异源对抗复审 [medium] 采纳,真病)────────────────────────────────────
|
|
32
|
+
* {@link kickEngineCapsProbe} 的代际闸挡的是「旧探测写脏 `capsByBase`」,而那道闸在 **probe 返回
|
|
33
|
+
* 之后**才跑。宿主的 caps 探测闭包里通常还挂着若干**读面 tee**(`noteEngineCapsForSqlEngine` /
|
|
34
|
+
* `noteEngineCapsForWriteProtection` …),它们是**在 probe 内部**、也就是在那道闸之前落地的 ⇒
|
|
35
|
+
* 一次慢的旧探测可以在 `invalidateEngineCaps` + 新探测都走完之后才返回,把**上一台引擎**的读数
|
|
36
|
+
* 重新写进那些 tee 表。屏面上的形态最坏:一行**肯定句**(「这台部署有 12 行写保护」)说的却是
|
|
37
|
+
* 一台已经不在的引擎。
|
|
38
|
+
*
|
|
39
|
+
* ── 用法(宿主侧)──────────────────────────────────────────────────────────────────────────
|
|
40
|
+
* 在**起探测的那一刻**抓一份号,tee 落地时原样交回:
|
|
41
|
+
* ```ts
|
|
42
|
+
* const gen = engineCapsGeneration(baseUrl)
|
|
43
|
+
* kickEngineCapsProbe(baseUrl, async () => {
|
|
44
|
+
* const caps = await client.capabilities()
|
|
45
|
+
* noteEngineCapsForWriteProtection(baseUrl, caps, { generation: gen }) // 陈旧 ⇒ 这一发被丢掉
|
|
46
|
+
* return caps
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
* 🔴 **不传 = 与本位到货之前逐字节同行为**(照写),所以它是 additive 供给、不是强制门 —— 端没跟上
|
|
50
|
+
* 之前不会有任何东西变坏,跟上之后那条竞态才真正关掉。
|
|
51
|
+
* 🔴 **tee 自己抓号是假判据**:陈旧那一发在 tee 时读到的是**新**代号,看起来永远是当代的。
|
|
52
|
+
* 号必须在**探测出生**那一刻抓。
|
|
53
|
+
*/
|
|
54
|
+
export declare function engineCapsGeneration(baseUrl: string): number;
|
|
28
55
|
/** 构造期 kick(async 幂等);probe = client.capabilities 薄闭包。 */
|
|
29
56
|
export declare function kickEngineCapsProbe(baseUrl: string, probe: () => Promise<unknown>): void;
|
|
30
57
|
/**
|
package/dist/engineCapsCache.js
CHANGED
|
@@ -53,6 +53,35 @@ const genByBase = new Map();
|
|
|
53
53
|
function capsGeneration(baseUrl) {
|
|
54
54
|
return genByBase.get(baseUrl) ?? 0;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* 该 base 的**当代号**(公面读口;0.60.0)。
|
|
58
|
+
*
|
|
59
|
+
* ── 它解决的是哪一件事(异源对抗复审 [medium] 采纳,真病)────────────────────────────────────
|
|
60
|
+
* {@link kickEngineCapsProbe} 的代际闸挡的是「旧探测写脏 `capsByBase`」,而那道闸在 **probe 返回
|
|
61
|
+
* 之后**才跑。宿主的 caps 探测闭包里通常还挂着若干**读面 tee**(`noteEngineCapsForSqlEngine` /
|
|
62
|
+
* `noteEngineCapsForWriteProtection` …),它们是**在 probe 内部**、也就是在那道闸之前落地的 ⇒
|
|
63
|
+
* 一次慢的旧探测可以在 `invalidateEngineCaps` + 新探测都走完之后才返回,把**上一台引擎**的读数
|
|
64
|
+
* 重新写进那些 tee 表。屏面上的形态最坏:一行**肯定句**(「这台部署有 12 行写保护」)说的却是
|
|
65
|
+
* 一台已经不在的引擎。
|
|
66
|
+
*
|
|
67
|
+
* ── 用法(宿主侧)──────────────────────────────────────────────────────────────────────────
|
|
68
|
+
* 在**起探测的那一刻**抓一份号,tee 落地时原样交回:
|
|
69
|
+
* ```ts
|
|
70
|
+
* const gen = engineCapsGeneration(baseUrl)
|
|
71
|
+
* kickEngineCapsProbe(baseUrl, async () => {
|
|
72
|
+
* const caps = await client.capabilities()
|
|
73
|
+
* noteEngineCapsForWriteProtection(baseUrl, caps, { generation: gen }) // 陈旧 ⇒ 这一发被丢掉
|
|
74
|
+
* return caps
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
* 🔴 **不传 = 与本位到货之前逐字节同行为**(照写),所以它是 additive 供给、不是强制门 —— 端没跟上
|
|
78
|
+
* 之前不会有任何东西变坏,跟上之后那条竞态才真正关掉。
|
|
79
|
+
* 🔴 **tee 自己抓号是假判据**:陈旧那一发在 tee 时读到的是**新**代号,看起来永远是当代的。
|
|
80
|
+
* 号必须在**探测出生**那一刻抓。
|
|
81
|
+
*/
|
|
82
|
+
export function engineCapsGeneration(baseUrl) {
|
|
83
|
+
return capsGeneration(baseUrl);
|
|
84
|
+
}
|
|
56
85
|
/** 构造期 kick(async 幂等);probe = client.capabilities 薄闭包。 */
|
|
57
86
|
export function kickEngineCapsProbe(baseUrl, probe) {
|
|
58
87
|
if (!baseUrl || capsByBase.has(baseUrl) || inFlight.has(baseUrl))
|
|
@@ -145,6 +145,18 @@ export declare function isInterruptedToolEndCode(code: string | undefined): bool
|
|
|
145
145
|
* 🔴 非布尔的 `selfOrchestration` 走的是 **400**,不是本码 —— 那是「值不对」不是「不给用」。
|
|
146
146
|
*/
|
|
147
147
|
export declare const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = "capability.self_orchestration_required";
|
|
148
|
+
/**
|
|
149
|
+
* 「**门把这次调用 park 了**」的机读码(core 5.52.0 `prepare-task` 的
|
|
150
|
+
* `abortResultDetails: () => ({ code: "gate.parked" })`;[4973] 契约:双短路点同席恒铸)。
|
|
151
|
+
*
|
|
152
|
+
* 🔴 **它是「这次调用没有在这里结算」的第二形**(engine 7.64.0 真机黑盒直证):审批窗走完 300s
|
|
153
|
+
* 未决而走 park 路径时,`tool_end` 帧上**没有 `gate` 字段** —— 只有这个码。门记录缺席在那一形上
|
|
154
|
+
* **不是**「记录坏了」,是「这次调用根本没结算过什么,它被 park 了」。⇒ 任何要回答「这次调用
|
|
155
|
+
* settle 了没有」的消费点必须**两形都认**:`gate.settlement` 在场那一形,与本码这一形。
|
|
156
|
+
* 🔴 单源:`hitl/frameRouter.ts` 的连坐/中断判据与 `gateOutcome.ts` 的停驻谓词读的是同一个常量 ——
|
|
157
|
+
* 两处各写一份字面量必漂(而漂的那一天两边对同一帧给出相反的判决)。
|
|
158
|
+
*/
|
|
159
|
+
export declare const GATE_PARKED_ERROR_CODE = "gate.parked";
|
|
148
160
|
/** 409 session-busy 的 canonical 码([2377]C-1,server main `049ff2c`,随 5.0.0 发)。 */
|
|
149
161
|
export declare const ACTIVE_RUN_BUSY_ERROR_CODE = "conflict.session_active_run";
|
|
150
162
|
/** 仲裁店不可达 ⇒ **真相未知**(core 1.397 三分的第三形;塌进 `not_landed` 是诚实缺陷)。 */
|
package/dist/engineErrorCodes.js
CHANGED
|
@@ -199,6 +199,19 @@ export function isInterruptedToolEndCode(code) {
|
|
|
199
199
|
* 🔴 非布尔的 `selfOrchestration` 走的是 **400**,不是本码 —— 那是「值不对」不是「不给用」。
|
|
200
200
|
*/
|
|
201
201
|
export const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = 'capability.self_orchestration_required';
|
|
202
|
+
// ── 门语义族 ──────────────────────────────────────────────────────────────────────────────────
|
|
203
|
+
/**
|
|
204
|
+
* 「**门把这次调用 park 了**」的机读码(core 5.52.0 `prepare-task` 的
|
|
205
|
+
* `abortResultDetails: () => ({ code: "gate.parked" })`;[4973] 契约:双短路点同席恒铸)。
|
|
206
|
+
*
|
|
207
|
+
* 🔴 **它是「这次调用没有在这里结算」的第二形**(engine 7.64.0 真机黑盒直证):审批窗走完 300s
|
|
208
|
+
* 未决而走 park 路径时,`tool_end` 帧上**没有 `gate` 字段** —— 只有这个码。门记录缺席在那一形上
|
|
209
|
+
* **不是**「记录坏了」,是「这次调用根本没结算过什么,它被 park 了」。⇒ 任何要回答「这次调用
|
|
210
|
+
* settle 了没有」的消费点必须**两形都认**:`gate.settlement` 在场那一形,与本码这一形。
|
|
211
|
+
* 🔴 单源:`hitl/frameRouter.ts` 的连坐/中断判据与 `gateOutcome.ts` 的停驻谓词读的是同一个常量 ——
|
|
212
|
+
* 两处各写一份字面量必漂(而漂的那一天两边对同一帧给出相反的判决)。
|
|
213
|
+
*/
|
|
214
|
+
export const GATE_PARKED_ERROR_CODE = 'gate.parked';
|
|
202
215
|
// ── 会话冲突族 ────────────────────────────────────────────────────────────────────────────────
|
|
203
216
|
/** 409 session-busy 的 canonical 码([2377]C-1,server main `049ff2c`,随 5.0.0 发)。 */
|
|
204
217
|
export const ACTIVE_RUN_BUSY_ERROR_CODE = 'conflict.session_active_run';
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/gateOutcome.ts — 一次工具门通过的**整条记录**:`tool_end.gate` 的三端共用窄读器(0.60.0)。
|
|
3
|
+
*
|
|
4
|
+
* ── 上游形变(engine ≥7.64.0 / sdk 8.4.0;wire BREAKING)────────────────────────────────────
|
|
5
|
+
* 7.63.0 及以前,一次调用怎么过门要从**四个正交可选词**里拼:`settledBy` / `resolution` /
|
|
6
|
+
* `autoDenied` / `approver`。那四个词的配对规则只活在注释里,消费端要靠**缺席**推语义(「有
|
|
7
|
+
* `timeout` 没有 `resolution` ⇒ 耐久 park 过期」)。7.64.0 起它是一条记录 {@link GateOutcomeView}:
|
|
8
|
+
* · `disposition` —— 放行,或被**哪一层**拒(`deniedBy` 八词);
|
|
9
|
+
* · `settlement?` —— 这次消费掉的等待**怎么结束的**(十二词 `kind`、形由词决定的 `who`、`when`,
|
|
10
|
+
* 人的拒绝另带 `note?`);
|
|
11
|
+
* · `origin?` —— 谁**问**的。
|
|
12
|
+
* `settlement` 与 `origin` **同在同缺**(它们说的是同一只 ask)。四个旧词**全删,无 alias**。
|
|
13
|
+
*
|
|
14
|
+
* ── 本模块存在的理由:判定归包,端不许自拼 ───────────────────────────────────────────────────
|
|
15
|
+
* 旧形上,「这一次是不是**审批窗自己走完**的拒绝」这个判定是端各自拼的一个两键合取
|
|
16
|
+
* (`settledBy === 'timeout' && resolution === 'window_expired'`),而拼错的方式有好几种、错法
|
|
17
|
+
* 静默(单锚 `settledBy` 会把「审批方违约」误收进来;写成不等式会把「不知道怎么拒的」折成
|
|
18
|
+
* 「窗自然结束」)。形变把这些配对规则搬进了**形状**,正是把判定收进包里的时机 ——
|
|
19
|
+
* 端从此调 {@link isApprovalWindowExpiredGate} / {@link isDenialLimitAutoDeniedGate},不再自拼。
|
|
20
|
+
*
|
|
21
|
+
* ── 判据纪律 ────────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
* 🔴 **缺席不是关于这次调用的事实**,禁读出任何语义。上游逐字列了**四种**在 wire 上不可分辨的
|
|
23
|
+
* 成因:①门根本没看见这次调用(未武装的门)/ ②延后重发 / ③reconcile 捡回的孤儿 / ④那条记录
|
|
24
|
+
* 过不了引擎的不变量筛、server **整条不上帧**。⇒ 一律退回 `isError` + 文案。
|
|
25
|
+
* ⚠️ **真机黑盒补的第五种成因**(engine 7.64.0 三路独立构造直证,与上面四种不同 —— 它有一个
|
|
26
|
+
* **可读的正面标记**):审批窗走完未决而走 **park** 路径时,`tool_end` 帧上没有 `gate`,只有
|
|
27
|
+
* `errorCode: "gate.parked"`。那一形不是「记录坏了」,是「这次调用根本没结算过什么」——
|
|
28
|
+
* 它由 {@link isGateParkedToolEnd} 单独回答,见那一处。
|
|
29
|
+
* 🔴 **开集读**:`settlement.kind` / `origin` / `deniedBy` 的词表属主都是 core。`deniedBy` 在
|
|
30
|
+
* wire 上确实是闭集(出集的记录 server 整条不投),但**本包不据此收窄**:窄读成枚举会在 core
|
|
31
|
+
* 加员当天把一个合法值判没,而端拿到的是「一个陌生的词」远好过「这一位不见了」。三处一律
|
|
32
|
+
* 「非空串即原样透传」,端的 `switch` **必须带 `default`**。
|
|
33
|
+
* 🔴 **`who` 的形由 `kind` 决定,本读器不重铸那张配对表**:它按结构原样窄读(`party` 非空串、
|
|
34
|
+
* `approver` / `window` 各自可选),把配对规则留在**引擎**那一侧。抄一份配对表 = 给自己立
|
|
35
|
+
* 第二个判官,而上游明说这张表是它的不变量。
|
|
36
|
+
* 🔴 `approver` 是 **UNTRUSTED-for-display**:转录不是认证(引擎只筛形,不鉴别、不比对、不回读),
|
|
37
|
+
* 可信度 = 那条审批通道的可信度;只渲染,绝不回喂模型/工具入参,更不许当鉴权判据。
|
|
38
|
+
* `note`(拒批人自己写的自由文本)同待遇。
|
|
39
|
+
*
|
|
40
|
+
* ── 🔴 同名不同义警告([same-name-different-meaning-crosses-layers])──────────────────────────
|
|
41
|
+
* 本包 `hitl/parkResolver.ts` 另有一个导出型也叫 `GateOutcome`,说的是**本包决断腿的结局**
|
|
42
|
+
* (`decided` / `aborted` / `failed`),与 sdk 的 `GateOutcome`(**引擎的门记录**)是两个语义面。
|
|
43
|
+
* 本模块的公面型因此一律带 `View` 后缀,两者在任何一处都不许互相赋值 —— 合并它们会让「这次门怎么
|
|
44
|
+
* 过的」与「这条决断链怎么收的」同形。
|
|
45
|
+
*/
|
|
46
|
+
import type { DeniedBy, Settlement } from '@sema-agent/sdk';
|
|
47
|
+
/**
|
|
48
|
+
* 这次门通过的**最终处置**。`deniedBy` 按**开集**读(型面用 sdk 的闭八词并 `string` 放宽,理由见
|
|
49
|
+
* 模块顶注)——`policy` / `hook` / `org` / `classifier` / `plan_mode` / `compliance` /
|
|
50
|
+
* `write_protection` / `ask_resolution`。
|
|
51
|
+
*/
|
|
52
|
+
export type GateDispositionView = {
|
|
53
|
+
kind: 'allowed';
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'denied';
|
|
56
|
+
deniedBy: DeniedBy | (string & {});
|
|
57
|
+
};
|
|
58
|
+
/** 结算方的身份座(形由 {@link SettlementView.kind} 决定,本读器不重铸那张配对表)。 */
|
|
59
|
+
export interface SettlementWhoView {
|
|
60
|
+
/** `person` / `engine` / `host` / `none`(开集读)。 */
|
|
61
|
+
party: string;
|
|
62
|
+
/** 审批通道**自报**的身份串;UNTRUSTED-for-display,缺席 = 通道没报名字。 */
|
|
63
|
+
approver?: string;
|
|
64
|
+
/** 引擎侧是**哪一只窗**(`approval_factory` / `denial_limit`);非引擎方恒缺席。 */
|
|
65
|
+
window?: string;
|
|
66
|
+
}
|
|
67
|
+
/** 一只 ask 在等的那次等待**怎么结束的**。 */
|
|
68
|
+
export interface SettlementView {
|
|
69
|
+
/** 闭十二词(core `SETTLEMENT_KINDS`),**按开集读**。 */
|
|
70
|
+
kind: Settlement['kind'] | (string & {});
|
|
71
|
+
who: SettlementWhoView;
|
|
72
|
+
/** epoch ms,写在结算发生的那一处。非有限数 ⇒ 缺席(绝不铸一个 0 时刻)。 */
|
|
73
|
+
when?: number;
|
|
74
|
+
/** 拒批人附的那句话。**没有 note 的拒 = 一句干脆的「不」**,不是「没说理由」。UNTRUSTED。 */
|
|
75
|
+
note?: string;
|
|
76
|
+
}
|
|
77
|
+
/** 一次工具门通过的整条记录(sdk `GateOutcome` 的**防御读**视图)。 */
|
|
78
|
+
export interface GateOutcomeView {
|
|
79
|
+
disposition: GateDispositionView;
|
|
80
|
+
/** 只在**这次通过真的结算了一只 ask** 时在场。与 {@link origin} 同在同缺。 */
|
|
81
|
+
settlement?: SettlementView;
|
|
82
|
+
/** 谁**问**的(core `ASK_ORIGINS`,7.6.0 起十词;**按开集读**)。与 {@link settlement} 同在同缺。 */
|
|
83
|
+
origin?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `tool_end` 帧(或本包投影出的 `tool_end_result` 内部臂、耐久行的 resolved outcome)→ 门记录。
|
|
87
|
+
*
|
|
88
|
+
* 入参是**帧**而不是 `frame.gate`,刻意如此:端不必知道这条记录挂在哪个键上(那正是下一次形变
|
|
89
|
+
* 会动的东西),也不必自己判「这一帧带不带门」。
|
|
90
|
+
*
|
|
91
|
+
* 🔴 `disposition` 是**唯一的承重座**:它读不出来 ⇒ 整只 `undefined`。一条只有 `settlement` 而
|
|
92
|
+
* 说不出「放行还是拒了」的记录,消费端能从它得到的只有误导。
|
|
93
|
+
* 🔴 **不校 I1–I4 那四条不变量**(`settlement` ⇔ `origin` 同在、`ask_resolution` ⇒ 有拒绝类结算……):
|
|
94
|
+
* 筛子的属主是引擎,server 对过不了筛的记录**整条不投**。包再校一遍,只会在上游合法演进的当天
|
|
95
|
+
* 把一条真记录判没,而本层给不出比「原样交给端」更好的降级动作(`wiring_manifest.autoMode`
|
|
96
|
+
* 那一段的反向先例是**同一条帧上的段内自洽**,不是跨系统的不变量,两者刻意不同处置)。
|
|
97
|
+
*/
|
|
98
|
+
export declare function gateOutcomeOf(frame: unknown): GateOutcomeView | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* 这次调用是**被门 park 了**吗 —— 「没在这里结算」的**第二形**(engine 7.64.0 真机黑盒直证)。
|
|
101
|
+
*
|
|
102
|
+
* 🔴 **为什么非有它不可**:审批窗走完(默认 300s)未决而走 park 路径时,`tool_end` 帧上
|
|
103
|
+
* **没有 `gate` 字段** —— 只有 `errorCode: "gate.parked"`。⇒ 单锚 {@link gateOutcomeOf} 的
|
|
104
|
+
* 消费点在这一形上什么都读不到,而这一形**恰恰是端最想说一句话的那一格**(「已转后台候批」)。
|
|
105
|
+
* 🔴 **两形合起来才是「这次调用没在这里结算」的完整判据**,端要判这件事写:
|
|
106
|
+
* `isApprovalWindowExpiredGate(gateOutcomeOf(f)) || isGateParkedToolEnd(f)`。
|
|
107
|
+
* 两形语义**不同**、不许互相顶替:前者是「窗走完了,拒了」,后者是「park 了,还没人决」。
|
|
108
|
+
* 🔴 **入参是整只帧**(与 {@link gateOutcomeOf} 同姿势):端不必知道这一位挂在哪个键上。
|
|
109
|
+
* 🔴 缺席不带语义:大多数 `tool_end` 都不带这个码,缺席 ≠「已经结算过了」。
|
|
110
|
+
*/
|
|
111
|
+
export declare function isGateParkedToolEnd(frame: unknown): boolean;
|
|
112
|
+
/** 这次门是不是**拒**(缺席 ⇒ `false` —— 缺席不带语义,见模块顶注)。 */
|
|
113
|
+
export declare function isDeniedGate(g: GateOutcomeView | undefined): boolean;
|
|
114
|
+
/** 拒这次的是**哪一层**(`policy` / `hook` / `org` / …;开集)。非拒 / 缺席 ⇒ `undefined`。 */
|
|
115
|
+
export declare function gateDeniedBy(g: GateOutcomeView | undefined): string | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* 这次结算是**谁**的(审批通道自报的身份串)。UNTRUSTED-for-display。
|
|
118
|
+
* 🔴 缺席 = 通道没报名字 —— 永不意味「没人批」,也永不意味「有人批」。
|
|
119
|
+
*/
|
|
120
|
+
export declare function gateApprover(g: GateOutcomeView | undefined): string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* 这次拒是**审批窗自己走完**的吗 —— 端此前自拼的那个两键合取(`settledBy === 'timeout' &&
|
|
123
|
+
* resolution === 'window_expired'`)的**继任单源**。
|
|
124
|
+
*
|
|
125
|
+
* 判据 = `settlement.kind === 'approval_window_expired'`(引擎自己的审批工厂窗,或同步宿主
|
|
126
|
+
* approver 报「我的窗到了」)。
|
|
127
|
+
* 🔴 **刻意不含另外两条窗词**:`denial_limit_window_expired` 是**分类器限额回落**的自动拒窗
|
|
128
|
+
* (端要渲的是另一句话,见 {@link isDenialLimitAutoDeniedGate}),`park_sla_expired` 是**耐久 park**
|
|
129
|
+
* 的 SLA 到期(结束它的是宿主的扫,不是这次调用在等的那只审批窗)。旧形上这三件事共用一个
|
|
130
|
+
* `settledBy:"timeout"`,分不出来正是要靠 `resolution` 补第二个键的原因;形变把它们分成了三个词,
|
|
131
|
+
* 合并读回去等于把刚修好的判别力扔掉。
|
|
132
|
+
* 🔴 **正面匹配,绝不写不等式**:不等式对缺席与任何未来新词都为真。
|
|
133
|
+
*/
|
|
134
|
+
export declare function isApprovalWindowExpiredGate(g: GateOutcomeView | undefined): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* 这次拒是**引擎的限额回落窗自己拒的**吗(不是人拒的)—— 端此前读的 `tool_end.autoDenied === true`
|
|
137
|
+
* 的**继任单源**。
|
|
138
|
+
*
|
|
139
|
+
* 判据 = `settlement.kind === 'denial_limit_window_expired'`(auto 模式分类器的拒绝上限回落窗:
|
|
140
|
+
* 那张必须真人批的卡在 `autoDenyAfterMs` 走完之后由**引擎**判 deny)。
|
|
141
|
+
* 🔴 它答的正是 {@link isApprovalWindowExpiredGate} 答不了的那一问:「这一次到期是**自动拒**收的场,
|
|
142
|
+
* 还是 park 了等人」。端要渲的两句话(「自动拒(限额回落)」vs「已转后台候批」)卡在这一格上。
|
|
143
|
+
* 🔴 **不据它自铸第二只定时器/第二张资格表**:窗的执行全在引擎,本位是**事后**的判别位。
|
|
144
|
+
*/
|
|
145
|
+
export declare function isDenialLimitAutoDeniedGate(g: GateOutcomeView | undefined): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* 这次结算是**耐久 park 的 SLA 到期**被宿主的扫判成拒的吗。
|
|
148
|
+
* 🔴 与上面两条并列而不合并:它说的是「那一行在队列里坐过了 deadline」,与「这次调用在等的审批窗
|
|
149
|
+
* 走完了」是两件不同的事,出路也不同(前者要重新提这一步,后者可以再决一次)。
|
|
150
|
+
*/
|
|
151
|
+
export declare function isParkSlaExpiredGate(g: GateOutcomeView | undefined): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* 这次门记录是**人**做的决定吗(`human_allowed` / `human_refused`)。
|
|
154
|
+
* 🔴 「人批了之后被复查否决」(上游不变量 I3)在这里读作:本谓词为 `true` **且** {@link isDeniedGate}
|
|
155
|
+
* 也为 `true` —— 那时 `deniedBy` 指的是复查那一层,approver 留在结算上,**不是** approver 的拒。
|
|
156
|
+
*/
|
|
157
|
+
export declare function isHumanSettledGate(g: GateOutcomeView | undefined): boolean;
|