@sema-agent/client-core 0.18.0 → 0.20.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/README.md +1 -1
- package/dist/adapt/arms.js +53 -11
- package/dist/adapt/ids.d.ts +1 -1
- package/dist/adapt/ids.js +11 -2
- package/dist/adapt/textStream.js +2 -2
- package/dist/adapt/toolCards.js +1 -1
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +2 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +58 -0
- package/dist/attachmentsWireCaps.d.ts +73 -10
- package/dist/attachmentsWireCaps.js +188 -14
- package/dist/controlRouter.d.ts +45 -8
- package/dist/controlRouter.js +53 -21
- package/dist/hitl/askGateWire.d.ts +1 -1
- package/dist/hitl/askGateWire.js +1 -1
- package/dist/hitl/frameRouter.js +3 -1
- package/dist/hitl/hitlBridge.d.ts +19 -2
- package/dist/hitl/hitlBridge.js +37 -0
- package/dist/hitl/hitlHostSurface.d.ts +27 -0
- package/dist/hitl/hitlHostSurface.js +50 -10
- package/dist/hitl/parkResolver.d.ts +8 -1
- package/dist/hitl/parkResolver.js +26 -1
- package/dist/hitl/toolApprovalWire.d.ts +41 -4
- package/dist/hitl/toolApprovalWire.js +67 -5
- package/dist/seam.d.ts +44 -1
- package/dist/seam.js +6 -0
- package/dist/seatContract.d.ts +68 -8
- package/dist/seatContract.js +57 -2
- package/package.json +1 -1
package/dist/seatContract.js
CHANGED
|
@@ -98,6 +98,26 @@ const isObject = (v) => typeof v === "object" && v !== null;
|
|
|
98
98
|
const isAbsent = (v) => typeof v === "undefined";
|
|
99
99
|
const optionalString = (v) => isAbsent(v) || typeof v === "string";
|
|
100
100
|
const inTuple = (tuple, v) => tuple.includes(v);
|
|
101
|
+
/**
|
|
102
|
+
* `ToolPermissionRequest.requestId` 的**域词表**(FIX⑥,2026-08-07)—— 三端唯一真源。
|
|
103
|
+
*
|
|
104
|
+
* 🔴 前缀不是装饰:它**就是**决断的路由依据(session-host 按它挑 wire 动词)。一个域外的 requestId
|
|
105
|
+
* 意味着「这条决断无处可去」,所以校验器对它 fail-loud 而不是放行(此前门只校「是不是串」,
|
|
106
|
+
* 于是 desktop 早就在用的 `plan:` 既不在枚举里、也不会被任何一处说出来)。
|
|
107
|
+
*/
|
|
108
|
+
export const TOOL_PERMISSION_REQUEST_ID_DOMAINS = [
|
|
109
|
+
"live:",
|
|
110
|
+
"durable:",
|
|
111
|
+
"question:",
|
|
112
|
+
"elicit:",
|
|
113
|
+
"plan:",
|
|
114
|
+
];
|
|
115
|
+
/** 读口:requestId → 它的域。未登记前缀 / 非串 ⇒ `undefined`(诚实缺席,绝不猜一个域)。 */
|
|
116
|
+
export function toolPermissionRequestIdDomain(requestId) {
|
|
117
|
+
if (typeof requestId !== "string")
|
|
118
|
+
return undefined;
|
|
119
|
+
return TOOL_PERMISSION_REQUEST_ID_DOMAINS.find((d) => requestId.startsWith(d));
|
|
120
|
+
}
|
|
101
121
|
export const TOCTOU_BINDING_KEYS = ["callId", "inputHash"];
|
|
102
122
|
/**
|
|
103
123
|
* 座位事件名的**闭集**(REF-CC-065):这是三端唯一的事件名真源。
|
|
@@ -227,6 +247,9 @@ const checkLocalSessionRecord = (s) => {
|
|
|
227
247
|
if (!okQueue)
|
|
228
248
|
return bad("queuedMessages must be an array of {uuid,text}", "queuedMessages");
|
|
229
249
|
}
|
|
250
|
+
if (!isAbsent(s.parked) && typeof s.parked !== "boolean") {
|
|
251
|
+
return bad("parked must be a boolean when present", "parked");
|
|
252
|
+
}
|
|
230
253
|
return true;
|
|
231
254
|
};
|
|
232
255
|
export function isLocalSessionRecord(s) {
|
|
@@ -247,6 +270,10 @@ const checkToolPermissionRequest = (r) => {
|
|
|
247
270
|
return bad("tool permission request is not an object");
|
|
248
271
|
if (typeof r.requestId !== "string")
|
|
249
272
|
return bad("requestId must be a string", "requestId");
|
|
273
|
+
// FIX⑥:域(前缀)就是决断的路由依据 —— 认不出域 = 这条决断无处可去,fail-closed 而不是放行。
|
|
274
|
+
if (toolPermissionRequestIdDomain(r.requestId) === undefined) {
|
|
275
|
+
return bad(`requestId must carry a registered domain prefix (${TOOL_PERMISSION_REQUEST_ID_DOMAINS.join("|")})`, "requestId");
|
|
276
|
+
}
|
|
250
277
|
if (typeof r.sessionId !== "string")
|
|
251
278
|
return bad("sessionId must be a string", "sessionId");
|
|
252
279
|
if (!isAbsent(r.toolName) && r.toolName !== null && typeof r.toolName !== "string") {
|
|
@@ -414,6 +441,22 @@ const checkSeatModelInfo = (m, index) => {
|
|
|
414
441
|
}
|
|
415
442
|
return true;
|
|
416
443
|
};
|
|
444
|
+
/** applyModelConfig ack 校验:布尔形 + reason 条件在场(ok:false ⇒ 必带非空 reason)。
|
|
445
|
+
* 🔴 错误消息零回显 req/ack 值——本 verb 载荷带 apiKey,校验层是回显面之一。 */
|
|
446
|
+
const checkApplyModelConfigResult = (v) => {
|
|
447
|
+
if (!isObject(v))
|
|
448
|
+
return bad("applyModelConfig result is not an object");
|
|
449
|
+
if (typeof v.ok !== "boolean")
|
|
450
|
+
return bad("ok must be a boolean", "ok");
|
|
451
|
+
if (typeof v.requiresRestart !== "boolean")
|
|
452
|
+
return bad("requiresRestart must be a boolean", "requiresRestart");
|
|
453
|
+
if (v.ok === false && (typeof v.reason !== "string" || v.reason.length === 0)) {
|
|
454
|
+
return bad("reason must be a non-empty string when ok is false", "reason");
|
|
455
|
+
}
|
|
456
|
+
if (v.reason !== undefined && typeof v.reason !== "string")
|
|
457
|
+
return bad("reason must be a string when present", "reason");
|
|
458
|
+
return true;
|
|
459
|
+
};
|
|
417
460
|
const checkSeatModelCatalog = (v) => {
|
|
418
461
|
if (!isObject(v))
|
|
419
462
|
return bad("model catalog is not an object");
|
|
@@ -545,6 +588,7 @@ export const LOCAL_SESSIONS_SPEC = {
|
|
|
545
588
|
searchSessions: { kind: "invoke", check: checkSessionSearchEnvelope },
|
|
546
589
|
uploadAttachment: { kind: "invoke", check: checkAttachmentUploadReceipt },
|
|
547
590
|
listModels: { kind: "invoke", check: checkSeatModelCatalog },
|
|
591
|
+
applyModelConfig: { kind: "invoke", check: checkApplyModelConfigResult },
|
|
548
592
|
onEvent: { kind: "event", check: checkLocalSessionEvent },
|
|
549
593
|
onToolPermissionRequest: { kind: "event", check: checkToolPermissionRequest },
|
|
550
594
|
},
|
|
@@ -570,6 +614,7 @@ export const SEAT_METHOD_NAMES = [
|
|
|
570
614
|
"searchSessions",
|
|
571
615
|
"uploadAttachment",
|
|
572
616
|
"listModels",
|
|
617
|
+
"applyModelConfig",
|
|
573
618
|
"onEvent",
|
|
574
619
|
"onToolPermissionRequest",
|
|
575
620
|
];
|
|
@@ -622,6 +667,7 @@ export const LOCAL_SESSION_RECORD_KEYS = [
|
|
|
622
667
|
"createdAt",
|
|
623
668
|
"lastActivityAt",
|
|
624
669
|
"queuedMessages",
|
|
670
|
+
"parked",
|
|
625
671
|
];
|
|
626
672
|
export const SEND_MESSAGE_REQUEST_KEYS = [
|
|
627
673
|
"sessionId",
|
|
@@ -672,7 +718,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
|
|
|
672
718
|
LocalSessionRecord: {
|
|
673
719
|
guard: "isLocalSessionRecord",
|
|
674
720
|
check: checkLocalSessionRecord,
|
|
675
|
-
validated: ["sessionId", "engineSessionId", "model", "title", "isRunning", "createdAt", "lastActivityAt", "queuedMessages"],
|
|
721
|
+
validated: ["sessionId", "engineSessionId", "model", "title", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked"],
|
|
676
722
|
unchecked: [],
|
|
677
723
|
},
|
|
678
724
|
ToolPermissionRequest: {
|
|
@@ -726,6 +772,11 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
|
|
|
726
772
|
validated: ["models", "defaultId"],
|
|
727
773
|
unchecked: [],
|
|
728
774
|
},
|
|
775
|
+
ApplyModelConfigResult: {
|
|
776
|
+
check: checkApplyModelConfigResult,
|
|
777
|
+
validated: ["ok", "requiresRestart", "reason"],
|
|
778
|
+
unchecked: [],
|
|
779
|
+
},
|
|
729
780
|
SendMessageAck: {
|
|
730
781
|
guard: "isSendMessageAck",
|
|
731
782
|
check: checkSendMessageAck,
|
|
@@ -811,7 +862,11 @@ export const SEAT_VALIDATOR_COVERAGE_EXEMPT = [
|
|
|
811
862
|
{
|
|
812
863
|
label: "LaneProof",
|
|
813
864
|
check: (v) => checkLaneProof(v, "laneProof"),
|
|
814
|
-
why: "判别式联合:必填键面随 `lane` 臂而变(subagent 要 parentToolCallId、workflow 要 via+key),而覆盖表的方法是「一个最小合法体 + 逐键投毒」—— 那套方法在它身上没有判别力(main 臂对另外两臂的伴随位本来就不该拒,投毒过了不代表漏校)
|
|
865
|
+
why: "判别式联合:必填键面随 `lane` 臂而变(subagent 要 parentToolCallId、workflow 要 via+key),而覆盖表的方法是「一个最小合法体 + 逐键投毒」—— 那套方法在它身上没有判别力(main 臂对另外两臂的伴随位本来就不该拒,投毒过了不代表漏校)。" +
|
|
866
|
+
"🔴 FIX④(2026-08-07 订正):本条此前自称判别力由「F 段**逐臂**投毒(空对象 / 非对象 / **未登记车道词**)」承担 —— 全树实扫,那三样里只有**空对象**与**非对象**真的存在(run-seat-contract-keys-test.mjs F 段)," +
|
|
867
|
+
"「未登记车道词」实际只钉了 `lane: 7`(非串,证明不了闭集成员判定在场),「逐臂」(subagent 缺 parentToolCallId / workflow 缺 via+key)则是**零覆盖**。也就是说这条豁免曾经拿三件不存在的判据给自己背书。" +
|
|
868
|
+
"现势(判据锚在真存在的东西上):① `LANE_PROOF_LANE_TUPLE` ⇄ seam.ts 车道联合的双向 `Covers`(本文件 `_seatContractPins`,加车道不跟 ⇒ 编译红);" +
|
|
869
|
+
"② run-client-core-pure-test.mjs **FIX7 段 ④** —— 未登记车道词 / subagent 缺伴随位 / workflow 缺 via 或 key 三条投毒 + 两条逐臂正控 + 一条「main 臂不被过紧误拒」负控,全部直接喂本条的 `check`。",
|
|
815
870
|
},
|
|
816
871
|
];
|
|
817
872
|
/** 双向覆盖钉:`Exclude` 两个方向都必须塌成 `never`,少一个键红、多一个键也红。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|