@sema-agent/client-core 0.64.1 → 0.65.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 +226 -0
- package/README.md +15 -7
- package/dist/adapt/arms.js +125 -7
- package/dist/adapt/ids.d.ts +22 -0
- package/dist/adapt/ids.js +29 -0
- package/dist/adapt/panelTasks.d.ts +22 -0
- package/dist/adapt/panelTasks.js +45 -0
- package/dist/adapt/textStream.js +6 -3
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +3 -0
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -10
- package/dist/adapter/downstream/eventToSdkMessage.js +207 -65
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +4 -4
- package/dist/adapter/downstream/terminalToSdkResult.js +43 -12
- package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +23 -2
- package/dist/adapter/downstream/turnUsageToModelUsage.js +7 -1
- package/dist/adapter/runStream.js +39 -2
- package/dist/adapter/types.d.ts +12 -0
- package/dist/autoModeUnavailable.d.ts +48 -79
- package/dist/autoModeUnavailable.js +70 -99
- package/dist/classifierStatus.d.ts +25 -71
- package/dist/classifierStatus.js +110 -105
- package/dist/cloudConfigWireCaps.js +16 -0
- package/dist/decideReceipt.d.ts +117 -0
- package/dist/decideReceipt.js +142 -0
- package/dist/engineErrorCodes.d.ts +32 -0
- package/dist/engineErrorCodes.js +42 -0
- package/dist/fleet/fleetProjection.d.ts +24 -1
- package/dist/fleet/fleetProjection.js +26 -1
- package/dist/fleetAgentPanelProjection.js +6 -1
- package/dist/gateVocabulary.d.ts +9 -1
- package/dist/gateVocabulary.js +46 -4
- package/dist/hitl/askGateWire.js +22 -1
- package/dist/hitl/gateLedger.d.ts +24 -0
- package/dist/hitl/gateLedger.js +8 -0
- package/dist/hitl/hitlBridge.js +14 -2
- package/dist/hitl/parkResolver.d.ts +23 -2
- package/dist/hitl/parkResolver.js +34 -6
- package/dist/hitl/toolApprovalWire.d.ts +94 -6
- package/dist/hitl/toolApprovalWire.js +127 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +16 -6
- package/dist/mcpWireCaps.d.ts +31 -0
- package/dist/mcpWireCaps.js +12 -0
- package/dist/notifications.js +11 -2
- package/dist/runTerminal.d.ts +48 -0
- package/dist/runTerminal.js +59 -0
- package/dist/seam.d.ts +131 -1
- package/dist/seam.js +22 -0
- package/docs/INTEGRATION-CLIENTS.md +934 -61
- package/package.json +2 -2
|
@@ -78,6 +78,8 @@
|
|
|
78
78
|
* 🔴 方向纪律:reason/note 只做归因,绝不参与裁决;缺席 ⇒ 现状字节不变。
|
|
79
79
|
*/
|
|
80
80
|
import { type GateCurrentPending, type HitlFailureStage, type HitlSafetyCode, type HitlClientLike } from './hitlBridge.js';
|
|
81
|
+
import type { ClassifierUnavailableView } from '../autoModeUnavailable.js';
|
|
82
|
+
import { type DecideReceiptView } from '../decideReceipt.js';
|
|
81
83
|
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, DENIAL_LIMIT_KINDS } from '@sema-agent/sdk';
|
|
82
84
|
import type { RuleSuggestion, ToolApprovalRespondAck, PersistedRuleAnchor as SdkPersistedRuleAnchor, RuleOfferMatch as SdkRuleOfferMatch, RuleOfferBatchMember as SdkRuleOfferBatchMember, RuleOfferUncoveredDetail as SdkRuleOfferUncoveredDetail, RuleOfferUncoveredReason as SdkRuleOfferUncoveredReason, AskOrigin as SdkAskOrigin, RuleOffersAbsence as SdkRuleOffersAbsence, DenialLimitKind as SdkDenialLimitKind, DenialLimitFallback as SdkDenialLimitFallback } from '@sema-agent/sdk';
|
|
83
85
|
/** fs 写权限 gate 判定:未来的一等 kind(tool_approval)或按 toolName(server 桥首批=fs 写三件,
|
|
@@ -98,10 +100,20 @@ export declare function isToolApprovalGate(gate: {
|
|
|
98
100
|
kind?: unknown;
|
|
99
101
|
toolName?: unknown;
|
|
100
102
|
} | undefined | null): boolean;
|
|
101
|
-
export type FsApprovalOutcome =
|
|
103
|
+
export type FsApprovalOutcome =
|
|
104
|
+
/** `receipt`(B-070 / L-200,0.65.0):这次 decide 的 **200 回执读数**
|
|
105
|
+
* ({@link import('../decideReceipt.js').DecideReceiptView})。两件事靠它:
|
|
106
|
+
* · **续跑挂在哪条 run 上** —— `handoffTaskId` 在场 ⇒ workflow 车道把续跑交给了**宿主新铸**的
|
|
107
|
+
* run(sdk README §9.0.0:「poll 它,不要盯那张卡」),重挂腿必须换挂过去;
|
|
108
|
+
* · **那个已决动作最后被怎么处置** —— `executionOutcome`(只在幂等回放形上)。
|
|
109
|
+
* 🔴 **缺席什么都不证明**:老 server / 终局形 / 回体读不动,三形同样缺席。尤其
|
|
110
|
+
* `executionOutcome` 缺席 = **未知**,禁读成 allowed(上游两处逐字,见 `decideReceipt.ts` 顶注)。
|
|
111
|
+
* 🔴 **它不是「门已解决」的证据**:200 只是投递受理(B-070 的病根就是把它当解决)。 */
|
|
112
|
+
{
|
|
102
113
|
kind: 'decided';
|
|
103
114
|
gatedCallId?: string | undefined;
|
|
104
115
|
denied?: boolean | undefined;
|
|
116
|
+
receipt?: DecideReceiptView | undefined;
|
|
105
117
|
} | {
|
|
106
118
|
kind: 'aborted';
|
|
107
119
|
gatedCallId?: string | undefined;
|
|
@@ -669,6 +681,30 @@ export interface ApprovalCardRequest {
|
|
|
669
681
|
* 本词是分诊/审计座,单独在场只说明「这类门提的问」,给不出计数。
|
|
670
682
|
*/
|
|
671
683
|
origin?: string;
|
|
684
|
+
/**
|
|
685
|
+
* cli L-174②(0.64.2;server ≥7.69.0 / core 7.10.0 #616)—— 「**问你是因为分类器这次跑不了**」的
|
|
686
|
+
* 事实,**双源合流**({@link governanceForced} 同形,两腿在场性可以不一致):
|
|
687
|
+
* · **活卡帧腿**:{@link ToolApprovalFrame.classifierUnavailable} 的窄读产物;
|
|
688
|
+
* · **durable park 行腿**:`PendingAction.tool_approval.classifierUnavailable` 的孪生位
|
|
689
|
+
* (core `checkpoint-store.d.ts` 逐字「the PARK twin of `AskRequest.classifierUnavailable`」),
|
|
690
|
+
* 经**同一把**窄读器 —— 两处**键路同形**,各写一份就是两份台账各漂各的。
|
|
691
|
+
*
|
|
692
|
+
* 🔴 **键路与 ask / park 行同形是有意的**:端不必为卡再学一个读法 —— `classifierUnavailableOf(req)`
|
|
693
|
+
* 直接吃这张卡(0.63.0 件⑧ 的读器,§27h2),措辞走它的孪生铸点 `classifierUnavailableDetail`。
|
|
694
|
+
* 🔴 **两层都按开集,显示层只多一条有出处的排除**(0.64.2 订正后两层判据几乎逐字相同):
|
|
695
|
+
* · **本层(过境)**只判形 —— `cause` 非空串即收,与 server 唯一铸点 `readClassifierUnavailable`
|
|
696
|
+
* 同判据。词表属主在 core,包在边界再校一次闭集,只会在 core 加词那天把一个**合法**值判没,
|
|
697
|
+
* 而丢的正是「这次不可用是新出现的那一类」这条信息(与 `origin`/`ruleOffersAbsence` 逐字同规);
|
|
698
|
+
* · **显示层**(`classifierUnavailableOf`)同样非空串即收,只排除**熔断轴独占**的词
|
|
699
|
+
* (今天 = `parse_error`;core 明说它不 stamp 到这一格)—— 未知词在那一层渲**兜底句**,
|
|
700
|
+
* 不是不渲。⚠️ 0.64.2 之前那一层是 unavailable **闭集**,于是一个合法的新成因词过得了本层、
|
|
701
|
+
* 却在显示层被判没(异源对抗复审逼出的真病;详见 INTEGRATION §29e)。
|
|
702
|
+
* 🔴 **显示元数据,不是裁决位**:不参与、也不改变本次决断的任何字节;缺席**不许**渲成
|
|
703
|
+
* 「分类器跑成了」(缺席三形不可分,见帧上同名键)。
|
|
704
|
+
* ⚠️ **退役日期已定**(上游发布记录 [6852]/[6853]):core 7.12.0 / server 7.70.0 起帧上这一格是**死键**,
|
|
705
|
+
* 事实位随拒绝面走;本包 0.65.0 改读那一面而**不删本键**——缺席臂当天就是正解。
|
|
706
|
+
*/
|
|
707
|
+
classifierUnavailable?: ClassifierUnavailableView;
|
|
672
708
|
}
|
|
673
709
|
/**
|
|
674
710
|
* 🔴 **拆缝口** —— 弹「三选卡」并等人的决断。壳 = vendored CC `PermissionRequest`;
|
|
@@ -852,6 +888,28 @@ export interface ToolApprovalFrame {
|
|
|
852
888
|
* durable 行 → 卡那条腿不 stamp,与 {@link requiresRealApproval} 同一格记账。
|
|
853
889
|
*/
|
|
854
890
|
denialLimitFallback?: DenialLimitFallback;
|
|
891
|
+
/**
|
|
892
|
+
* **server ≥7.69.0 / core 7.10.0 #616**(cli L-174②;**ADDITIVE**,`"tool_approval"` only。真发直证 =
|
|
893
|
+
* engine 7.69.0 fixture `@sema-agent/server/dist/tool-approval.js:847` 的条件 stamp + 同包
|
|
894
|
+
* `approval-card.js:244` `readClassifierUnavailable`(`z.object({cause: z.string().min(1).max(200)}).strict()`,
|
|
895
|
+
* 与 `card_json` 铸点 `:315` 同一只读器);上游发布记录 [6863] ③ 表逐字「additive;缺席不是断言;`cause`
|
|
896
|
+
* core 闭三词但 **wire 上 string**」)——这只 ask 是因为 **auto 模式分类器这一轮跑不了**才问人的,
|
|
897
|
+
* `cause` 说的是「为什么没跑成」(core `AUTO_MODE_UNAVAILABLE_CAUSES`)。
|
|
898
|
+
*
|
|
899
|
+
* 🔴 **显示元数据,不是裁决位**:引擎在门上已经判完了,没有任何东西读它决定放行与否。
|
|
900
|
+
* 🔴 **缺席不是断言**:缺席同时覆盖「分类器答上了」「这只 ask 没资格走分类器」「本部署没接分类器」
|
|
901
|
+
* 三形(core d.ts 逐字:read presence, never absence)—— 端**禁**读成「分类器好着呢」。
|
|
902
|
+
* 🔴 **echo-only**:server 顶注逐字写明它不据本键做任何裁决;据它在下游自铸第二套熔断/回落判据
|
|
903
|
+
* = 同一语义面两个写者(源头修复纪律)。本包同律 —— 本批只做过境,**不做熔断相关任何事**。
|
|
904
|
+
* ⚠️ **它是一个已定退役日期的键**(上游发布记录 [6852] server 侧落点表逐字:「ask 帧 `classifierUnavailable`
|
|
905
|
+
* **死键**;事实位随 deny 载荷 … 三面投影(tool_approval 帧 / card_json / inbox 行)**退役**,
|
|
906
|
+
* `permissionDenied.classifierUnavailable` 走 `tool_end.gate` 既有透传」)⇒
|
|
907
|
+
* **core 7.11.x 仍写;core 7.12.0 / server 7.70.0 起缺席**。本包 0.65.0 按 [6853] 的排期改从
|
|
908
|
+
* 拒绝面渲,**码不删**(死键保留型):缺席臂那天就是正解,退役一个 additive 键不该逼三端各改一次。
|
|
909
|
+
* ⚠️ **sdk 8.8.0 的运行期锚 `TOOL_APPROVAL_FRAME_KEYS`(27 项)尚无本键** ⇒ 镜像**领先**一代,
|
|
910
|
+
* 对账门里挂着一条带退出条件的 `AHEAD_OF_ANCHOR` 登记(sdk 8.9.x 同拍补上那天它自红逼删)。
|
|
911
|
+
*/
|
|
912
|
+
classifierUnavailable?: ClassifierUnavailableView;
|
|
855
913
|
/**
|
|
856
914
|
* server ≥7.13.0(#144 / core 5.25.0,[3438] 接力契约 / [3443] 主件;**ADDITIVE**,
|
|
857
915
|
* `"tool_approval"` only。来源锚 = engine fixture `@sema-agent/server/dist/tool-approval.d.ts`
|
|
@@ -871,10 +929,13 @@ export interface ToolApprovalFrame {
|
|
|
871
929
|
* 🔴 **UNTRUSTED-for-display,且不是身份通道** —— 详见
|
|
872
930
|
* {@link ApprovalCardRequest.persistedRuleShadowed} 的同名警示(下游删除规则面不许拿它当规则身份)。
|
|
873
931
|
*
|
|
874
|
-
* 耐久路(park 行)
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
932
|
+
* 耐久路(park 行)的对偶是行上的 **`riskDescriptor.shadowedRule`**(server `redactPendingDisclosures`
|
|
933
|
+
* 逐字脱敏后随 `projectPendingForWire` 上 wire;⚠️ **键名与本处顶层键不同**,那正是它长期被漏掉的原因)。
|
|
934
|
+
* ✅ **0.64.2 起已消费**(件⑥):`surfaceFsApprovalAndDecide` 把它投到**同一个卡位**
|
|
935
|
+
* {@link ApprovalCardRequest.persistedRuleShadowed} —— 两条腿说的是同一件事,端读一个形。
|
|
936
|
+
* ⚠️ 本段 0.28.0 原文写的是「shadowedRule 的耐久对偶仍未消费——那一路是独立的一件」,那句话到 0.64.2
|
|
937
|
+
* 不再成立:它不是一件「独立的展示件」,而是壳**让位臂的输入** —— 不投的直接后果是耐久路上一条
|
|
938
|
+
* 「规则清不掉」的 ask 被当成普通 ask 自动放行(cli B-058 离线真跑证实)。
|
|
878
939
|
*/
|
|
879
940
|
persistedRuleShadowed?: string;
|
|
880
941
|
/**
|
|
@@ -1038,7 +1099,7 @@ export interface ToolApprovalDelegation {
|
|
|
1038
1099
|
* `TOOL_APPROVAL_FRAME_KEYS` 比对——SDK additive 增键时对账当天红,不再人肉追平。
|
|
1039
1100
|
* 下面两个类型钉保证镜像与 interface 本身不可能漂移(少键/多键都是编译错)。
|
|
1040
1101
|
*/
|
|
1041
|
-
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "origin", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
|
|
1102
|
+
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "classifierUnavailable", "origin", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
|
|
1042
1103
|
/**
|
|
1043
1104
|
* 子代帧判别:显式键 fromSubagent(core 1.378 RB-39②)优先;缺席退 sourceTaskId 在场性权宜式
|
|
1044
1105
|
* (server 1.258 [1549]①3,旧代际兼容)。
|
|
@@ -1264,6 +1325,33 @@ export declare function readToolApprovalRespondRefusal(err: unknown): ToolApprov
|
|
|
1264
1325
|
* `AgentEvent` 的臂了(durable 腿也回放),所以结构识别与 union 收窄两条路都成立;本函数仍按
|
|
1265
1326
|
* 结构读(不依赖类型收窄),因为它同时服务 raw SSE 与 durable 回放两条入口。 */
|
|
1266
1327
|
export declare function isToolApprovalFrame(ev: unknown): ev is ToolApprovalFrame;
|
|
1328
|
+
/**
|
|
1329
|
+
* `denialLimitFallback` 的**窄读器**(S-114,0.59.0)—— wire 是 JSON:注入面 / 旧 server / 比本包新
|
|
1330
|
+
* 一版的 server 都可能给别的形,坏形一律降**缺席**(与 `readToolApprovalDelegation` 同族纪律)。
|
|
1331
|
+
*
|
|
1332
|
+
* 🔴 **四成员全必填,缺一整只丢**(与 server `approval-card.ts` 的 `readDenialLimitFallback`
|
|
1333
|
+
* `.strict()` 同判据,与 core 的形一字不差):半只对象上的 `consecutive` 会被人当成**真实的
|
|
1334
|
+
* 连续拒次数**读,而它可能只是一个恰好在场的键。「一格空白的计数」比不渲这张卡更坏。
|
|
1335
|
+
* 🔴 **两个计数与窗按「有限非负数」判,不折 0**:`Number.isFinite` 单独会放行负数,而三者的定义域
|
|
1336
|
+
* 按上游契约本就非负(计数是次数,窗是 ms 且 `0` = 不武装,是**合法读数**不是缺席)。
|
|
1337
|
+
* NaN / Infinity / 负数 = 坏形降缺席,**绝不猜**(同文件 `delegation.depth` 的旧教训)。
|
|
1338
|
+
* 🔴 **`limit` 按开集读**(非空串即收):闭二词表 `DENIAL_LIMIT_KINDS` 只作再导出给端数,
|
|
1339
|
+
* 包内**不拿它做判定** —— 词表属主是 core,抄一份就是给自己立第二个判官(B-025 的病形);
|
|
1340
|
+
* server 侧已按闭集拒过词表外的值,包再校一遍只会在 core 加员当天把一个合法值判没。
|
|
1341
|
+
* ⚠️ 这条与「四成员全必填」不矛盾:必填说的是**在场性**,开集说的是**取值域**。
|
|
1342
|
+
*
|
|
1343
|
+
* 🆕 **0.64.2 起在公面上**(cli L-174③;此前是模块私有):壳侧此前自持一份同判据的副本
|
|
1344
|
+
* (`src/sema/askFrameNotes.ts`),那是**第二个判官** —— 上游哪天在四成员上加一位、或把某一位的
|
|
1345
|
+
* 定义域改了,两份判据各漂各的,而屏上看到的是哪一份取决于素材走了哪条腿。导出的用途正是让那份
|
|
1346
|
+
* 副本整只退役:端读**包内窄读产物**({@link ApprovalCardRequest.denialLimitFallback})时不必再判
|
|
1347
|
+
* 一遍,端拿到**裸帧/裸行**时也有同一把读器可用。
|
|
1348
|
+
* 🔴 **导出的是读器,不是许可**:`autoDenyAfterMs` 仍然只许渲倒计时(窗的执行全在引擎),
|
|
1349
|
+
* 这一条不因它上了公面而松动。
|
|
1350
|
+
* ⚠️ **与 server `.strict()` 的一格差**(L-120③,如实写在两侧):对象上**多出的成员**本读器剥后
|
|
1351
|
+
* 保留四键,server 侧整只拒收 —— 包比 server 宽一格,方向是「多键不误杀」。端不要把「包读出来了」
|
|
1352
|
+
* 当成「server 也会收」。
|
|
1353
|
+
*/
|
|
1354
|
+
export declare function readDenialLimitFallback(v: unknown): DenialLimitFallback | undefined;
|
|
1267
1355
|
/**
|
|
1268
1356
|
* `ruleOffers`(server ≥7.46.0 的判别联合)的结构读。
|
|
1269
1357
|
*
|
|
@@ -89,6 +89,9 @@ import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
|
89
89
|
import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
|
|
90
90
|
import { observeCancelByDeny, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
91
91
|
import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
|
|
92
|
+
// B-070 / L-200(0.65.0):`/decide` 200 回执的**单一读面**(`handoffTaskId` 换挂句柄 +
|
|
93
|
+
// `executionOutcome` 缺席=未知)。判定归包,端零自拼。
|
|
94
|
+
import { readDecideReceipt } from '../decideReceipt.js';
|
|
92
95
|
// ── sdk 8.2.0 单源闭词表(S-134 锚②;0.58.0 提货批 / B-025 修根)──────────────────────────────
|
|
93
96
|
// 🔴 **值级 import,不是 type-only**:三张 `as const` 数组是**运行期**判据(窄读器按它判 kind /
|
|
94
97
|
// match 是不是闭集成员)。本包此前把 `match` 的二词闭集**手抄**在读器里,而 core design/382 /
|
|
@@ -334,6 +337,44 @@ parkGatedCallId) {
|
|
|
334
337
|
// (run-durable-card-display-keys-test.mjs 的 NOT_PROJECTED 账),上游补位后按 probeCause 的
|
|
335
338
|
// 双源合流形跟批。
|
|
336
339
|
const durableProbeCause = pending.riskDescriptor?.probeCause;
|
|
340
|
+
// ── 0.64.2 件⑥(cli B-058 让位臂的输入;**durable 腿两位对偶**)────────────────────────────
|
|
341
|
+
// 病:本处「行 → 卡」重铸此前只投 5 位,而这两位在 `/v1/approvals` 行上**今天就有**:
|
|
342
|
+
// · `persistedRuleShadowed` ← 行的 **`riskDescriptor.shadowedRule`**(⚠️ **键名与活卡帧顶层不同**:
|
|
343
|
+
// 帧上是顶层 `persistedRuleShadowed`,行上 server 把它放在 riskDescriptor 下并逐字脱敏 ——
|
|
344
|
+
// engine 7.69.0 fixture `http/routes/approvals-assistant.js` 的 `redactPendingDisclosures`
|
|
345
|
+
// `typeof rd.shadowedRule === "string" ? redactSecrets(rd.shadowedRule) : undefined`,
|
|
346
|
+
// 再由 `projectPendingForWire` 随行上 wire。名字不同正是它此前被漏掉的原因);
|
|
347
|
+
// · `ruleOffersAbsence` ← 行上**同名**键(server 补投那天零改动生效;今天两只 checkpoint 店的
|
|
348
|
+
// `listPending` 还没把它扁平化上来 —— 与本文件 `classifierUnavailable` 的 durable 腿同一格记账)。
|
|
349
|
+
// 🔴 **为什么是行为面而不只是补一格显示**:这两位是壳「让位臂」的输入 —— 活卡腿早就有,durable 腿
|
|
350
|
+
// 因为不投,一条**被记住的 allow 规则清不掉的 ask** 在耐久路上看起来就是一条普通 ask,于是被
|
|
351
|
+
// 自动 decide approve(cli B-058 离线真跑:default 车道 + `Bash(grep:*)` + 带 shadowedRule 的
|
|
352
|
+
// park 行 ⇒ **零卡 + decide approve**)。包只保证这两位到得了卡口,让位判定归端。
|
|
353
|
+
// 🔴 判据与活卡腿逐字同族:**非空串才 stamp**(空串是坏值不是「空规则」),词表**不校**(开集),
|
|
354
|
+
// 坏形/缺席 ⇒ 键不 stamp(**绝不折成空串**:一格空白的规则解释比没有解释更坏)。
|
|
355
|
+
// 🔴 stamp 的是**原文字节**(不 trim、不截、不改写),与活卡腿同族口径 —— 展示值一改写就不再是
|
|
356
|
+
// 「你那条规则」的原文;长度夹取归端。UNTRUSTED-for-display,且**不是身份通道**
|
|
357
|
+
// (见 {@link ApprovalCardRequest.persistedRuleShadowed} 的同名警示)。
|
|
358
|
+
const durableShadowedRule = pending.riskDescriptor?.shadowedRule;
|
|
359
|
+
const durableRuleOffersAbsence = pending.ruleOffersAbsence;
|
|
360
|
+
// cli L-174②(0.64.2):durable 腿的「分类器跑不了」孪生位 —— 载体 = 行上的 `classifierUnavailable`
|
|
361
|
+
// (core `checkpoint-store.d.ts` 逐字「the PARK twin of `AskRequest.classifierUnavailable`」,写在
|
|
362
|
+
// `PendingAction.tool_approval` 上;行是 `pendingAction` 的**扁平投影**,`ruleOffers`/`hasBidiControls`
|
|
363
|
+
// 都是同一条路落到顶层的)。sdk 8.8.0 的 `PendingCheckpoint` 尚无本键声明 ⇒ **结构视图读**
|
|
364
|
+
// (与同函数 `ruleOffers` / `riskDescriptor.probeCause` 同款姿势),类型半场候 sdk 班车。
|
|
365
|
+
// 🔴 **与 `ruleEvidence` 的「刻意零 stamp」不同裁,理由要说清**:那一位在上游**根本没有耐久对偶**,
|
|
366
|
+
// 读 `riskDescriptor.ruleEvidence` 是在猜一个载体名(猜对了是白写,猜错了是拿另一个量冒充);
|
|
367
|
+
// 本位的对偶**是上游逐字声明的**,键名同形、语义同源 —— 不是猜。
|
|
368
|
+
// ⚠️ **今天的供给缺口,如实记(不是「接通了」)**:engine 7.69.0 的两只 checkpoint 店
|
|
369
|
+
// (`plugins/local-checkpoint-store.js:144 listPending` / `plugins/checkpoint-store-sql.js:500
|
|
370
|
+
// listPending`)把 `pendingAction` 扁平化时**没有**带这一位(`/v1/assistant/inbox` 那条腿带了,
|
|
371
|
+
// 但那不是本腿吃的 `/v1/approvals` 行)⇒ 本腿在今天的引擎上**恒零命中**,server 补投那天零改动
|
|
372
|
+
// 生效。⚠️ **别把 `governanceForced` 也读进这一格**(异源对抗复审 r2 非阻断③ 订正):那一位
|
|
373
|
+
// 确实不在 `listPending` 的产物里,但同包 `http/routes/approvals-assistant.js` 的
|
|
374
|
+
// `projectPendingForWire` 会用 `governanceOriginOf(...)` **现算并补到行上**,所以它今天是**到得了**
|
|
375
|
+
// 本腿的;`riskDescriptor.shadowedRule` 同理(那条路由里的 `redactPendingDisclosures` 脱敏后随行
|
|
376
|
+
// 上 wire,件⑥ 消费的正是它)。本位是这三者里**唯一**今天真的没有供给的一格。
|
|
377
|
+
const durableClassifierUnavailable = readClassifierUnavailable(pending.classifierUnavailable);
|
|
337
378
|
deps.onPresented?.(); // L-80:真要交给卡口了才算呈过(规则直决 / 取件失败都不走到这一行)
|
|
338
379
|
const card = await surfaceApprovalCard({
|
|
339
380
|
toolName,
|
|
@@ -350,6 +391,20 @@ parkGatedCallId) {
|
|
|
350
391
|
...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
|
|
351
392
|
...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
|
|
352
393
|
...(isWireRecordCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
|
|
394
|
+
// cli L-174②(0.64.2):与活卡腿**同一把窄读器、同一个卡位**(键路同形 ⇒ 端一把读器吃两条腿)。
|
|
395
|
+
// 缺席 ⇒ 键不 stamp;判据与供给缺口见上面那段头注。
|
|
396
|
+
...(durableClassifierUnavailable !== undefined
|
|
397
|
+
? { classifierUnavailable: durableClassifierUnavailable }
|
|
398
|
+
: {}),
|
|
399
|
+
// 件⑥(0.64.2):durable 腿的两位对偶(判据与「为什么是行为面」见上面那段头注)。
|
|
400
|
+
// 🔴 落位是**活卡腿同一个卡位**(`persistedRuleShadowed` / `ruleOffersAbsence`),不是新键:
|
|
401
|
+
// 两条腿说的是同一件事,端读一个形;载体名不同只是上游两条腿各自的形,不该漏到卡面上。
|
|
402
|
+
...(typeof durableShadowedRule === 'string' && durableShadowedRule.trim() !== ''
|
|
403
|
+
? { persistedRuleShadowed: durableShadowedRule }
|
|
404
|
+
: {}),
|
|
405
|
+
...(typeof durableRuleOffersAbsence === 'string' && durableRuleOffersAbsence !== ''
|
|
406
|
+
? { ruleOffersAbsence: durableRuleOffersAbsence }
|
|
407
|
+
: {}),
|
|
353
408
|
// #348(0.44.0)durable 腿的对偶:行上**本来就存着**这个值(server `parked-decide.ts`;SDK
|
|
354
409
|
// `PendingCheckpoint.toolCallId?: string | null` 直证)。与活卡帧腿同一个卡位、同一条缺席纪律 ——
|
|
355
410
|
// `null`(SDK 声明的第二种缺席形)与空串一并降键缺席,绝不把 `null` 折成串。
|
|
@@ -377,8 +432,10 @@ parkGatedCallId) {
|
|
|
377
432
|
// 修复前行为,fail-soft 不更糟)。
|
|
378
433
|
if (card.allowSession) {
|
|
379
434
|
try {
|
|
380
|
-
await bridge.decideTool({ decision: 'approve', remember: 'session', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
381
|
-
|
|
435
|
+
const raw = await bridge.decideTool({ decision: 'approve', remember: 'session', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
436
|
+
// B-070:回执**原样读一次**(缺席即缺席);它不是「已解决」的证据,见 `receipt` 位顶注。
|
|
437
|
+
const receipt = readDecideReceipt(raw);
|
|
438
|
+
return { kind: 'decided', gatedCallId, ...(receipt !== undefined ? { receipt } : {}) };
|
|
382
439
|
}
|
|
383
440
|
catch (e) {
|
|
384
441
|
// 🔴 [2393] hitl-F4(2026-08-02):回退臂**只**兜「老 server 不识别 remember ⇒ 400 未知键」
|
|
@@ -409,8 +466,9 @@ parkGatedCallId) {
|
|
|
409
466
|
hostLog('debug', `liveToolApprovalWire: decide(approve+remember) failed (${String(e)}) — falling back to plain approve`);
|
|
410
467
|
}
|
|
411
468
|
}
|
|
412
|
-
await bridge.decideTool({ decision: 'approve', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
413
|
-
|
|
469
|
+
const raw = await bridge.decideTool({ decision: 'approve', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
470
|
+
const receipt = readDecideReceipt(raw);
|
|
471
|
+
return { kind: 'decided', gatedCallId, ...(receipt !== undefined ? { receipt } : {}) };
|
|
414
472
|
}
|
|
415
473
|
catch (e) {
|
|
416
474
|
// #363:S-02 指路键原样透传(读不出 ⇒ 键不铸,形状逐字节不变)。见 FsApprovalOutcome 同名位。
|
|
@@ -436,8 +494,11 @@ parkGatedCallId) {
|
|
|
436
494
|
// 规则自动 deny(rule-deny / dontAsk / headless fail-closed)的真实拒因此前只能落 debug 日志,
|
|
437
495
|
// 引擎与运维队列上看到的都是那句无差别的缺省文案。缺席/空白 ⇒ 缺省,现状字节不变。
|
|
438
496
|
// 🔴 归因不改裁决:无论 reason 是什么,这一支恒 `decision:'deny'`。
|
|
439
|
-
await bridge.decideTool({ decision: 'deny', reason: denyReasonForWire(card.reason, `task ${taskId}`) ?? DEFAULT_DENY_REASON }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
440
|
-
|
|
497
|
+
const raw = await bridge.decideTool({ decision: 'deny', reason: denyReasonForWire(card.reason, `task ${taskId}`) ?? DEFAULT_DENY_REASON }, gatedCallId, signal ? { signal } : undefined, pending);
|
|
498
|
+
// B-070 同形存量:拒绝腿与放行腿**一次改齐**(两条 decide 腿都要带回执 —— 一条 deny 同样
|
|
499
|
+
// 可能走 workflow 车道换挂,也同样可能拿到幂等回放形的 `executionOutcome`)。
|
|
500
|
+
const receipt = readDecideReceipt(raw);
|
|
501
|
+
return { kind: 'decided', gatedCallId, denied: true, ...(receipt !== undefined ? { receipt } : {}) };
|
|
441
502
|
}
|
|
442
503
|
catch (e) {
|
|
443
504
|
// #363:与 allow 臂**同形同源**(同形存量清剿:两条 decide 失败腿一次改齐,不许只修当格)。
|
|
@@ -498,6 +559,14 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
|
|
|
498
559
|
// 🔴 本键与 `requiresRealApproval` 是孪生键,但两者在镜像里各占一格 —— 合并会让「只有一位在场」
|
|
499
560
|
// 的坏形无处显形。
|
|
500
561
|
'denialLimitFallback',
|
|
562
|
+
// cli L-174②(client-core 0.64.2):server 7.69.0 起真发 `classifierUnavailable`(core 7.10.0 #616;
|
|
563
|
+
// engine 7.69.0 fixture `tool-approval.js:847` 条件 stamp + `approval-card.js:244` 窄读器直证)。
|
|
564
|
+
// ⚠️ 与 `ruleOffersAbsence`/`denialLimitFallback` **不同形**、与 `persistedRuleShadowed`/`probeCause`
|
|
565
|
+
// 同形:sdk 8.8.0 的运行期锚**尚无**本键(node 直读实证:锚 27 项)⇒ 这是一次**领先**,进对账门的
|
|
566
|
+
// AHEAD_OF_ANCHOR 带退出条件登记(sdk 8.9.x 同拍补上那天那条登记自红逼删,回到逐元素相等)。
|
|
567
|
+
// 🔴 本键有**已定的退役日期**(server 7.70.0 / core 7.12.0 起三面投影退役,上游发布记录 [6852]),但退役
|
|
568
|
+
// 走的是「缺席」而不是「删码」([6853] 死键保留型)⇒ 镜像里这一格不因退役而动。
|
|
569
|
+
'classifierUnavailable',
|
|
501
570
|
// S-125③/#564(client-core 0.59.0):server 7.57.0 起真发 `origin`(core 7.5.0 `ASK_ORIGINS`
|
|
502
571
|
// 八词;engine 7.60.0 fixture 直证)。同上:sdk 8.3.0 锚已含本键 ⇒ 追平,不进领先表。
|
|
503
572
|
'origin',
|
|
@@ -898,8 +967,19 @@ function isWireRecordCarrier(v) {
|
|
|
898
967
|
* 包内**不拿它做判定** —— 词表属主是 core,抄一份就是给自己立第二个判官(B-025 的病形);
|
|
899
968
|
* server 侧已按闭集拒过词表外的值,包再校一遍只会在 core 加员当天把一个合法值判没。
|
|
900
969
|
* ⚠️ 这条与「四成员全必填」不矛盾:必填说的是**在场性**,开集说的是**取值域**。
|
|
970
|
+
*
|
|
971
|
+
* 🆕 **0.64.2 起在公面上**(cli L-174③;此前是模块私有):壳侧此前自持一份同判据的副本
|
|
972
|
+
* (`src/sema/askFrameNotes.ts`),那是**第二个判官** —— 上游哪天在四成员上加一位、或把某一位的
|
|
973
|
+
* 定义域改了,两份判据各漂各的,而屏上看到的是哪一份取决于素材走了哪条腿。导出的用途正是让那份
|
|
974
|
+
* 副本整只退役:端读**包内窄读产物**({@link ApprovalCardRequest.denialLimitFallback})时不必再判
|
|
975
|
+
* 一遍,端拿到**裸帧/裸行**时也有同一把读器可用。
|
|
976
|
+
* 🔴 **导出的是读器,不是许可**:`autoDenyAfterMs` 仍然只许渲倒计时(窗的执行全在引擎),
|
|
977
|
+
* 这一条不因它上了公面而松动。
|
|
978
|
+
* ⚠️ **与 server `.strict()` 的一格差**(L-120③,如实写在两侧):对象上**多出的成员**本读器剥后
|
|
979
|
+
* 保留四键,server 侧整只拒收 —— 包比 server 宽一格,方向是「多键不误杀」。端不要把「包读出来了」
|
|
980
|
+
* 当成「server 也会收」。
|
|
901
981
|
*/
|
|
902
|
-
function readDenialLimitFallback(v) {
|
|
982
|
+
export function readDenialLimitFallback(v) {
|
|
903
983
|
if (!isWireRecordCarrier(v))
|
|
904
984
|
return undefined;
|
|
905
985
|
const d = v;
|
|
@@ -918,6 +998,38 @@ function readDenialLimitFallback(v) {
|
|
|
918
998
|
autoDenyAfterMs: d.autoDenyAfterMs,
|
|
919
999
|
};
|
|
920
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* `classifierUnavailable` 的**过境窄读器**(cli L-174②,0.64.2)—— 两条卡腿(活卡帧 / durable park
|
|
1003
|
+
* 行)共用的**唯一**一把:两处键路同形,各写一份就是两份台账各漂各的。
|
|
1004
|
+
*
|
|
1005
|
+
* 🔴 **判据与 server 的唯一铸点同源**(engine 7.69.0 fixture `approval-card.js:240/244`:
|
|
1006
|
+
* `z.object({ cause: z.string().min(1).max(200) }).strict()` + envelope 的 safeParse)——
|
|
1007
|
+
* · 载体非对象 / null / 数组 ⇒ 缺席;
|
|
1008
|
+
* · `cause` 非串 / 空串 ⇒ 缺席(空壳等于说「分类器坏了但说不出为什么」,比不说更坏 —— server
|
|
1009
|
+
* d.ts 顶注逐字);
|
|
1010
|
+
* · **`cause` 按开集收**:词表属主在 core,server 侧已按 `min(1)` 之外零词表校验过,包再抄一张
|
|
1011
|
+
* 闭集表只会在 core 加词那天把一个合法值判没,而丢的正是「这次不可用是新出现的那一类」
|
|
1012
|
+
* 这条信息(与 `origin` / `ruleOffersAbsence` / `denialLimitFallback.limit` 逐字同规)。
|
|
1013
|
+
* 🔴 **只交 `cause` 一格**(与 `classifierUnavailableOf` 的读数形逐字相同):顺手把载体上别的键
|
|
1014
|
+
* 带出来会长成第二份 ask 读面。
|
|
1015
|
+
* ⚠️ **与 server `.strict()` 的一格差**(与 {@link readDenialLimitFallback} 同一条,如实写):
|
|
1016
|
+
* 载体上**多出的成员**本读器剥后保留 `cause`,server 侧整只拒收 —— 包宽一格,方向是「多键不误杀」。
|
|
1017
|
+
* 🔴 **过境 ≠ 显示**:本读器答「这条事实到不到得了卡口」;「渲哪一句」由公面的
|
|
1018
|
+
* `classifierUnavailableOf` / `classifierUnavailableDetail` 答 —— 那一层同样非空串即收,只排除
|
|
1019
|
+
* **熔断轴独占**的词(`parse_error`)。⚠️ 0.64.2 之前那一层是 unavailable **闭集**,于是一个合法的
|
|
1020
|
+
* 新成因词过得了本层却在显示层被判没;订正后两层判据几乎逐字相同,差的只有那条有出处的排除
|
|
1021
|
+
* —— 见 {@link ApprovalCardRequest.classifierUnavailable} 与 INTEGRATION §29e。
|
|
1022
|
+
* **不导出**:它是本模块的边界窄化器,唯一消费点是下面两个卡口调用;导出会给公面再加一个 `unknown`
|
|
1023
|
+
* 入参签名(typeshape 门的 unknown-出境棘轮 +1),而它的行为由两条腿的端到端素材覆盖。
|
|
1024
|
+
*/
|
|
1025
|
+
function readClassifierUnavailable(v) {
|
|
1026
|
+
if (!isWireRecordCarrier(v))
|
|
1027
|
+
return undefined;
|
|
1028
|
+
const cause = v.cause;
|
|
1029
|
+
if (typeof cause !== 'string' || cause === '')
|
|
1030
|
+
return undefined;
|
|
1031
|
+
return { cause };
|
|
1032
|
+
}
|
|
921
1033
|
/**
|
|
922
1034
|
* `match` 是不是**闭词表成员** —— 表来自 sdk 8.2.0 `RULE_OFFER_MATCHES`(`exact` / `prefix` /
|
|
923
1035
|
* `wildcard` / `subpath`),**本包不再手抄字面量**。
|
|
@@ -1341,6 +1453,14 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
1341
1453
|
// 🔴 stamp 的是**窄读产物**而不是原对象:端拿到的四座恒是有限非负数 + 非空 limit 串,
|
|
1342
1454
|
// 不必各自再写一遍同样的判断(三端各写一遍 = 三份会漂的判官)。
|
|
1343
1455
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
1456
|
+
// ── cli L-174②(0.64.2):「问你是因为分类器这次跑不了」的事实透传 ──────────────────────────
|
|
1457
|
+
// 经 `readClassifierUnavailable` 单点窄读(判据本体在该函数顶注:`cause` 非空串即收、**开集**、
|
|
1458
|
+
// 坏形降缺席)。stamp 的是**窄读产物** `{cause}`,与 durable 腿**同一把读器**(键路同形)。
|
|
1459
|
+
// 🔴 缺席 ⇒ 键不 stamp,**绝不折成任何肯定话**:缺席同时覆盖「分类器答上了」「这只 ask 没资格
|
|
1460
|
+
// 走分类器」「本部署没接分类器」三形,端禁读成「分类器好着呢」。
|
|
1461
|
+
// ⚠️ 本键 core 7.12.0 / server 7.70.0 起是死键(帧上恒缺席,事实位随拒绝面走)—— 那天这条
|
|
1462
|
+
// stamp 自然零命中,**不需要**也**不许**在这里加任何熔断/回落判据(echo-only)。
|
|
1463
|
+
...((c) => (c !== undefined ? { classifierUnavailable: c } : {}))(readClassifierUnavailable(frame.classifierUnavailable)),
|
|
1344
1464
|
// ── S-125③/#564(0.59.0):ask 出身透传 ────────────────────────────────────────────────────
|
|
1345
1465
|
// 非空串才 stamp,词表**不校**(同 ruleOffersAbsence)。🔴 缺席 ⇒ 键不 stamp,**绝不折成
|
|
1346
1466
|
// `"policy"`**:那是一个正面事实(出自部署 ToolPolicy),把「老引擎没报」折进去 = 替引擎编话。
|
package/dist/index.d.ts
CHANGED
|
@@ -230,6 +230,7 @@ export * from './engineWireTarget.js';
|
|
|
230
230
|
export * from './principalWire.js';
|
|
231
231
|
export * from './wireErrorTriage.js';
|
|
232
232
|
export * from './resumeRefusalCopy.js';
|
|
233
|
+
export * from './decideReceipt.js';
|
|
233
234
|
export * from './sessionMap.js';
|
|
234
235
|
export * from './detachWire.js';
|
|
235
236
|
export * from './workflowMonitor.js';
|
package/dist/index.js
CHANGED
|
@@ -192,18 +192,22 @@ export * from './toolRoster.js';
|
|
|
192
192
|
// 0.63.0(L-167;core 7.9.x):engine_notice 码册与 audience 表的镜像 + mcp.injection_dropped
|
|
193
193
|
// 事实窄读器。措辞**不镜像**(上游立了 single_mint 契约:core 铸句子,消费端用它给的 message)。
|
|
194
194
|
export * from './engineNoticeCodes.js';
|
|
195
|
-
// 0.63.0 件⑧(core 7.10.0 #616):「这只 ask
|
|
196
|
-
//
|
|
195
|
+
// 0.63.0 件⑧(core 7.10.0 #616;**0.65.0 随 core 7.12.0 收窄**):「这只 ask 是因为分类器跑不了
|
|
196
|
+
// 才问人」的事实读器 + 措辞铸点。成因表两词(`error`/`timeout`),`cause` 开集读;判据轴的
|
|
197
|
+
// `parse_error` 判缺席(core 明说它不 stamp 到这一格)。熔断族(`AUTO_MODE_BREAKER_CAUSES` /
|
|
198
|
+
// `classifierBreakerOf` / `ClassifierBreakerView`)随 core 7.12.0 的 Removed(BREAKING)整只退役。
|
|
197
199
|
export * from './autoModeUnavailable.js';
|
|
198
200
|
// 0.64.0 件①(core 7.10.0 `OpenAICompletionsCompat.thinkingFormat`):一条 OpenAI-completions
|
|
199
201
|
// 车道的模型「会不会思考、关不关得掉、用哪种拼法关」的三端公共探测 + 落笔纯函数。
|
|
200
202
|
// 🔴 零 fetch:网络那半场经注入的 `ProbeSend` 端口,凭证一个字节都不经过本包(与
|
|
201
203
|
// `model/catalogLoader.ts` 的 `CatalogFetchJson` 同一条纪律)。试关序是**公面承诺**,门钉死。
|
|
202
204
|
export * from './modelCapabilityProbe.js';
|
|
203
|
-
// 0.64.0 件②(
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
205
|
+
// 0.64.0 件②(**0.65.0 两处同批改口**:随 core 7.12.0 收掉熔断族 + B-071 把 `armed` 与
|
|
206
|
+
// `available` 拆成两个词 ⇒ 三态):auto 分类器的**状态面**读器与措辞铸点
|
|
207
|
+
// (诊断行 / 模型设置页 / 权限卡状态栏)。与 `autoModeUnavailable.ts` 的**卡面**那一问刻意分家:
|
|
208
|
+
// 卡答「这一刻为什么问我」,状态答「这个会话上分类器现在是什么状态」。
|
|
209
|
+
// 🔴 会话轴(熔断记录)随 core 7.12.0 的 Removed(BREAKING)整只退役;同批 B-071 拆词 ⇒ 三态 =
|
|
210
|
+
// 本轮不可用 / 这条腿武装了 / 本轮真的跑过(后者只从 `denial_limit_fallback` 那条肯定事实得出)。
|
|
207
211
|
export * from './classifierStatus.js';
|
|
208
212
|
export * from './engineToolLabelStore.js';
|
|
209
213
|
export * from './fleetTaskDesc.js';
|
|
@@ -350,6 +354,12 @@ export * from './wireErrorTriage.js';
|
|
|
350
354
|
// 本口答「该对人说什么」(文案),`preflight_rejected` 的窗与可等性**转调**前者不复制判定。
|
|
351
355
|
// 壳侧对位 = cli `src/sema/resumeRefusalCopy.ts`(1.0.101 起改薄成适配层)。
|
|
352
356
|
export * from './resumeRefusalCopy.js';
|
|
357
|
+
// · decideReceipt:B-070 / L-200(0.65.x)—— `/decide` **答了什么**的三端单一读面。
|
|
358
|
+
// 🔴 两件事靠它,而两件都不是「门解决了没有」(200 只是**投递受理**,sdk README §9.0.0):
|
|
359
|
+
// ① `handoffTaskId` —— workflow 车道把续跑交给**宿主新铸**的 run(「poll 它,不要盯那张卡」);
|
|
360
|
+
// ② `executionOutcome` —— 那个已决动作**最后被怎么处置**;🔴 **缺席 = 未知,禁读成 allowed**。
|
|
361
|
+
// 三条 workflow 车道拒绝码的一句人话同居于此(单铸;码常量在 `engineErrorCodes.ts`)。
|
|
362
|
+
export * from './decideReceipt.js';
|
|
353
363
|
// · sessionMap:「客户端会话 id ↔ 引擎会话 id」映射单一键形 + merge 判定(A-028.12;存储经
|
|
354
364
|
// SessionMapStorePort 归端 —— cli 文件锁/原子写,web localStorage)。
|
|
355
365
|
export * from './sessionMap.js';
|
package/dist/mcpWireCaps.d.ts
CHANGED
|
@@ -12,6 +12,21 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Trust: the SHELL only PROJECTS the user's local config; the SERVICE single-user gate (mcpInjectionHonored
|
|
14
14
|
* = requirePrincipal!==true) decides whether to honor it (fail-closed on multi-tenant). So projecting is safe.
|
|
15
|
+
*
|
|
16
|
+
* ── 🔴 0.64.2:逐键白名单重建的**丢键面**(cli L-167①,包侧缺陷)───────────────────────────────
|
|
17
|
+
* 本文件的两条 transport 臂是**逐键重建**(`{name, transport:{…}, allowTools}`),不是 `...c` 全展开。
|
|
18
|
+
* 这是有意的 —— 全展开会把用户 `.mcp.json` 里的任意键原样送上 wire,而 `TaskRequest.mcpServers` 是
|
|
19
|
+
* 引擎的请求面,多一个引擎不认的键既没人判形也没人负责。代价是本仓一贯要根治的那个病形:
|
|
20
|
+
* **上游 additive 加了一个真键,而这条闭集重建静默把它剥掉**([C170] / `hasBidiControls` /
|
|
21
|
+
* 窗三键的同一形)。`toolFaces` 就是这么丢的 —— 运维在 `.mcp.json` 上声明的 per-tool 面(写围栏读的
|
|
22
|
+
* `pathTarget` 那一格)到不了引擎,而**两侧看起来都对**:配置文件里写着,引擎那边只是「没有声明」。
|
|
23
|
+
* ⇒ 处置 = **按上游的透明键表逐名透传**,不是改成全展开。表的属主是 `@sema-agent/settings-schema`
|
|
24
|
+
* 的 mcp 段(`McpServerSpec` 上 `z.unknown()` 的那些键;1.10.0 实装两员:`source` 1.7.2 / `toolFaces`
|
|
25
|
+
* 1.9.0),判据逐字是「解析透明」:形的校验属**引擎摄入侧**,本层一个字节不改也不拒。
|
|
26
|
+
* 🔴 **`source` 今天不在本条腿上**:它是**部署面**(`config.d/mcp.json` / `/effective.mcp`)的分组
|
|
27
|
+
* 标签,消费者是 server;**请求面**的 `McpServerSpec`(sdk 8.8.0 `types.d.ts`)上根本没有这个键 ——
|
|
28
|
+
* 往这里塞它是给 wire 加一个引擎不认的键,而不是透传一件事实。上游哪天把它加到请求面上,
|
|
29
|
+
* 本表按同一条规矩加一行(门里那条「透明键表 ⇄ 本腿透传集」的对账当天红)。
|
|
15
30
|
*/
|
|
16
31
|
import type { TaskRequest } from '@sema-agent/sdk';
|
|
17
32
|
/** The wire shape `TaskRequest.mcpServers` carries. SDK 0.0.44's index no longer re-exports the
|
|
@@ -33,6 +48,22 @@ export interface McpConfigLike {
|
|
|
33
48
|
url?: string;
|
|
34
49
|
headers?: Record<string, string>;
|
|
35
50
|
allowTools?: string[];
|
|
51
|
+
/**
|
|
52
|
+
* **透明键**(settings-schema 1.9.0 `McpServerSpec.toolFaces`,`z.unknown()`;core 7.8.0 design/388
|
|
53
|
+
* 片 1 F7 / server ≥7.66.0)—— 这台 server 的 per-tool 面覆盖层,键 = 工具**裸名**,值形
|
|
54
|
+
* `{ family?, pathTarget?, renderHints?, ruleFace? }`(表的属主是 core,见 sdk `McpToolFace`)。
|
|
55
|
+
*
|
|
56
|
+
* 🔴 **类型是 `unknown`,而且是有意的**:上游把这一格声明成解析透明,理由逐字是「这一域
|
|
57
|
+
* all-or-nothing,任一字段解析拒都会让整套 mcp 服务器静默消失」。本层照抄那条判据 ——
|
|
58
|
+
* **不校形、不改写、不按名剥**,坏形也原样过境。
|
|
59
|
+
* 🔴 **坏形的处置在引擎,不在这里**:JSON 形写坏或出现读器不认的键 ⇒ 引擎让**整条 server 不进**
|
|
60
|
+
* 并在请求腿 `mcp_injection_dropped{dropped:[name]}` 点名;形合而语义坏(`pathTarget.param` 不在
|
|
61
|
+
* 工具物化后的 schema 上)⇒ 引擎拒**那一只**工具并出 `config.tool_face_invalid` 通告。
|
|
62
|
+
* 包在这里替引擎判一次形,只有两种结局:判严了 ⇒ 运维声明的保护被**静默**吞掉(比引擎响亮拒
|
|
63
|
+
* 更坏);判松了 ⇒ 白写一道会漂的第二判官。
|
|
64
|
+
* 缺席 ⇒ 键不铸(`.mcp.json` 是 JSON,`undefined` 只可能来自「没写」)。
|
|
65
|
+
*/
|
|
66
|
+
toolFaces?: unknown;
|
|
36
67
|
}
|
|
37
68
|
/** Map one CC `.mcp.json` server → core `McpServerSpec`, or null when the transport isn't core-supported
|
|
38
69
|
* (sse/sse-ide/ws/ws-ide) or required fields are missing. */
|
package/dist/mcpWireCaps.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
/** Server caps mirror the service merge cap (task-mcp.ts: ≤32, fail-closed on malformed). */
|
|
2
2
|
export const MCP_CAPS = { items: 32 };
|
|
3
|
+
/**
|
|
4
|
+
* 透明键的**原样过境**格(0.64.2)—— 见 {@link McpConfigLike.toolFaces} 与模块顶注。
|
|
5
|
+
* 键在场即铸(`undefined` = 没写),值一个字节不动;`null` / 串 / 数组这些坏形照样过境,由引擎判。
|
|
6
|
+
* 🔴 cast 到 wire 形是**类型面的**让位,不是运行期校验:请求面 `McpServerSpec.toolFaces` 的声明是
|
|
7
|
+
* `Record<string, McpToolFace>`(sdk 8.8.0),而透明键的契约是「什么 JSON 值都可能到」——
|
|
8
|
+
* 在这里按声明形收窄就等于把坏形剥掉,正是本批要修的病。
|
|
9
|
+
*/
|
|
10
|
+
function transparentKeys(c) {
|
|
11
|
+
return c.toolFaces !== undefined ? { toolFaces: c.toolFaces } : {};
|
|
12
|
+
}
|
|
3
13
|
/** Map one CC `.mcp.json` server → core `McpServerSpec`, or null when the transport isn't core-supported
|
|
4
14
|
* (sse/sse-ide/ws/ws-ide) or required fields are missing. */
|
|
5
15
|
export function mcpConfigToSpec(name, c) {
|
|
@@ -19,6 +29,7 @@ export function mcpConfigToSpec(name, c) {
|
|
|
19
29
|
...(c.env ? { env: c.env } : {}),
|
|
20
30
|
},
|
|
21
31
|
...(c.allowTools && c.allowTools.length > 0 ? { allowTools: c.allowTools } : {}),
|
|
32
|
+
...transparentKeys(c),
|
|
22
33
|
};
|
|
23
34
|
}
|
|
24
35
|
if (kind === 'http') {
|
|
@@ -32,6 +43,7 @@ export function mcpConfigToSpec(name, c) {
|
|
|
32
43
|
...(c.headers ? { headers: c.headers } : {}),
|
|
33
44
|
},
|
|
34
45
|
...(c.allowTools && c.allowTools.length > 0 ? { allowTools: c.allowTools } : {}),
|
|
46
|
+
...transparentKeys(c),
|
|
35
47
|
};
|
|
36
48
|
}
|
|
37
49
|
// sse / sse-ide / ws / ws-ide → core McpServerSpec has no such transport (StreamableHTTP only) → skip.
|
package/dist/notifications.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import { hostEnv } from './hostEnv.js';
|
|
8
8
|
import { unrefTimer } from './unrefTimer.js';
|
|
9
9
|
import { clearEnginePanelTaskResident, publishEngineAgentPanelEvent, } from './engineAgentPanelStore.js';
|
|
10
|
+
// L-215②(0.65.0;core [6908]):「非成功终局」的**单铸谓词** —— 面板 settle 的 `isError` 位与
|
|
11
|
+
// 三端自己的后台白名单必须读同一个判据;内联一个 `=== 'failed' || === 'killed'` 就是第二份会漂的台账。
|
|
12
|
+
import { isTerminalNotSuccess, isTerminalStatus } from './runTerminal.js';
|
|
10
13
|
function escapeXml(v) {
|
|
11
14
|
return v.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
12
15
|
}
|
|
@@ -165,7 +168,9 @@ export function parseTranscriptNotificationSeeds(messages) {
|
|
|
165
168
|
continue;
|
|
166
169
|
const external = /\btype="external"/.test(attrs);
|
|
167
170
|
renderedKeys.push(`${external ? 'external:' : ''}${taskId}:${status}`);
|
|
168
|
-
|
|
171
|
+
// L-215② 族扫(0.65.0):终局判据走单铸谓词 —— 修前这里手抄三词,core [6908] 的 `blocked`
|
|
172
|
+
// 漏在外面 ⇒ 一条已经渲过的 `blocked` 通知不进 `notifiedKeys`,重开会话后**再投一次**。
|
|
173
|
+
if (!external && isTerminalStatus(status)) {
|
|
169
174
|
notifiedKeys.push(taskId);
|
|
170
175
|
}
|
|
171
176
|
seeded++;
|
|
@@ -893,7 +898,11 @@ export function enqueueBgChildNotification(n) {
|
|
|
893
898
|
publishEngineAgentPanelEvent({
|
|
894
899
|
kind: 'end',
|
|
895
900
|
taskId: n.taskId,
|
|
896
|
-
|
|
901
|
+
// L-215②(0.65.0):读**单铸谓词**而不是内联两词 —— 修前这里只认 `failed`/`killed`,
|
|
902
|
+
// 而 core [6908] 的 `blocked` 是 **agent 自报的终态**(不是等人)⇒ 一条自报走不下去的
|
|
903
|
+
// 后台 run 在面板上被 settle 成**成功**。🔴 `suspended`/`needs_review` 仍不在表里
|
|
904
|
+
// (那两词是「等一次人的决定」,判成终局会把一条正等着你的 run 在面板上判死)。
|
|
905
|
+
isError: isTerminalNotSuccess(n.status),
|
|
897
906
|
});
|
|
898
907
|
}
|
|
899
908
|
catch {
|
package/dist/runTerminal.d.ts
CHANGED
|
@@ -192,3 +192,51 @@ export declare function runTerminalCode(read: RunTerminalRead | null): string |
|
|
|
192
192
|
* 非 paused 的读数、以及两个来源都答不出的 paused,一律 `false`(更宽的那一句兜底)。
|
|
193
193
|
*/
|
|
194
194
|
export declare function isReviewPark(read: RunTerminalRead | null): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* **非成功终局**的状态词(闭集;0.65.0 新铸,L-215② / core [6908])——「这条 run **不会再自己动了**,
|
|
197
|
+
* 而且它没有成功」。
|
|
198
|
+
*
|
|
199
|
+
* 🔴 三员各有出处,且 **`blocked` 与 `suspended`/`needs_review` 的分界是本表存在的全部理由**
|
|
200
|
+
* (core [6908] 定谳):
|
|
201
|
+
* · `failed` —— 引擎自报失败;
|
|
202
|
+
* · `killed` —— 外力终止;
|
|
203
|
+
* · `blocked` —— **agent 自报的终态**:它自己判定这条 run 走不下去了(core `terminal.blocked`
|
|
204
|
+
* 的 `reason` 是它的说明)。🔴 它**不是「等人」** —— 这正是修前被漏掉的那一格;
|
|
205
|
+
* · 🔴 **`suspended` / `needs_review` 刻意不在表里**:那两个词是**等一次人的决定**,run 还活着、
|
|
206
|
+
* 决定给了就继续跑。把它们读成「非成功终局」会把一条**正等着你**的 run 在面板上判死,
|
|
207
|
+
* 而用户从此不知道有一张卡在等他 —— 与本表要修的方向相反的同一类错。
|
|
208
|
+
*
|
|
209
|
+
* 🔴 形制:`Object.freeze` 的数组,不是 `ReadonlySet`(同 `RESUME_RETRY_LATER_CODES` 的已定谳病形:
|
|
210
|
+
* `ReadonlySet` 只在类型面只读,而判定查的就是公面上这同一个实例)。
|
|
211
|
+
*/
|
|
212
|
+
export declare const TERMINAL_NOT_SUCCESS_STATUSES: readonly string[];
|
|
213
|
+
/**
|
|
214
|
+
* 一个状态词是不是**非成功终局**({@link TERMINAL_NOT_SUCCESS_STATUSES} 的成员)。
|
|
215
|
+
*
|
|
216
|
+
* **单铸谓词**(L-215②):本包的通知链(面板 settle 的 `isError` 位)与三端各自的后台白名单
|
|
217
|
+
* 读的必须是**同一个判据** —— 修前包里是一处内联的 `status === 'failed' || status === 'killed'`,
|
|
218
|
+
* 壳里另有一份白名单,两处各写各的,于是 core 加 `blocked` 那天两边一起漏,而漏的后果是
|
|
219
|
+
* **面板把一条自报失败的 run 渲成成功**。
|
|
220
|
+
*
|
|
221
|
+
* 🔴 **开集外的词答 `false`,而 `false` 不等于「成功」**:它只说「这个词不在本端认得的非成功终局
|
|
222
|
+
* 表里」。消费点要判「成功」必须去读成功那一侧的正向证据({@link readRunTerminal} 的
|
|
223
|
+
* `completed` 臂),不能拿本谓词的取反当成功判据 —— 那正是「不认识的终态词折成成功」那条
|
|
224
|
+
* 已定谳的事故形。
|
|
225
|
+
* 🔴 非串 / 空串 ⇒ `false`(同上:是「答不出」,不是「成功」)。
|
|
226
|
+
*/
|
|
227
|
+
export declare function isTerminalNotSuccess(status: unknown): boolean;
|
|
228
|
+
/**
|
|
229
|
+
* **终局**的状态词(闭集;0.65.0 新铸)—— 成功那一员加上 {@link TERMINAL_NOT_SUCCESS_STATUSES}
|
|
230
|
+
* 的三员。答的是「这条 run 还会不会自己动」,与上一张表答的「它成没成功」是**两个问题**,所以
|
|
231
|
+
* 两张表分开、后者由前者派生(加员只有一处要改)。
|
|
232
|
+
*
|
|
233
|
+
* 🔴 同样**不含** `suspended` / `needs_review`:那两个词是「等一次人的决定」——run 没有结束。
|
|
234
|
+
*/
|
|
235
|
+
export declare const TERMINAL_STATUSES: readonly string[];
|
|
236
|
+
/**
|
|
237
|
+
* 一个状态词是不是**终局**({@link TERMINAL_STATUSES} 的成员)。
|
|
238
|
+
*
|
|
239
|
+
* 用在「这条通知/这条行是不是可以收摊了」这一问上(通知去重的 seed 扫描、后台行 settle 门)。
|
|
240
|
+
* 🔴 表外词 ⇒ `false` = 「本端认不出这是个终局」,**不是**「它还在跑」;要判「在跑」请读正向证据。
|
|
241
|
+
*/
|
|
242
|
+
export declare function isTerminalStatus(status: unknown): boolean;
|