@sema-agent/client-core 0.101.0 → 0.102.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +12 -7
  3. package/dist/adapt/toolCards.d.ts +2 -1
  4. package/dist/adapt/toolCards.js +20 -6
  5. package/dist/adapt.d.ts +1 -1
  6. package/dist/adapt.js +1 -1
  7. package/dist/adapter/downstream/eventToSdkMessage.js +6 -68
  8. package/dist/adapter/downstream/wiringManifestView.d.ts +1 -0
  9. package/dist/adapter/downstream/wiringManifestView.js +138 -0
  10. package/dist/compensations.js +1 -1
  11. package/dist/displayUntrusted.d.ts +8 -4
  12. package/dist/displayUntrusted.js +769 -96
  13. package/dist/engineRowRef.js +1 -0
  14. package/dist/fleet/fleetProjection.js +1 -1
  15. package/dist/i18n/messages.en.d.ts +38 -0
  16. package/dist/i18n/messages.en.js +38 -0
  17. package/dist/i18n/messages.zh.js +40 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.js +5 -0
  20. package/dist/interactiveHalt.d.ts +3 -1
  21. package/dist/interactiveHalt.js +2 -0
  22. package/dist/interactiveInterrupt.d.ts +2 -0
  23. package/dist/interactiveInterrupt.js +15 -0
  24. package/dist/modsFace.d.ts +8 -0
  25. package/dist/modsFace.js +59 -0
  26. package/dist/ownKey.d.ts +1 -0
  27. package/dist/ownKey.js +15 -0
  28. package/dist/printToolResultFrame.js +1 -1
  29. package/dist/request/printNotification.js +1 -1
  30. package/dist/request/taskRequest.d.ts +21 -0
  31. package/dist/request/taskRequest.js +123 -7
  32. package/dist/seam.js +1 -1
  33. package/dist/seatContract.d.ts +22 -6
  34. package/dist/seatContract.js +35 -6
  35. package/dist/sessionBackground.d.ts +165 -0
  36. package/dist/sessionBackground.js +781 -0
  37. package/dist/sessionBackgroundHandoff.d.ts +54 -0
  38. package/dist/sessionBackgroundHandoff.js +321 -0
  39. package/dist/sessionMap.js +4 -1
  40. package/dist/settingsCredentialKeyPaths.js +2 -17
  41. package/dist/thinkingLatch.d.ts +69 -0
  42. package/dist/thinkingLatch.js +255 -0
  43. package/dist/thinkingLatchWords.d.ts +7 -0
  44. package/dist/thinkingLatchWords.js +4 -0
  45. package/dist/toolResult.js +2 -24
  46. package/docs/INTEGRATION-CLIENTS.md +309 -10
  47. package/package.json +1 -1
  48. package/dist/argvFlagValue.d.ts +0 -8
  49. package/dist/argvFlagValue.js +0 -37
@@ -1,7 +1,8 @@
1
1
  import { appendOwnElement, readOwnDataValue } from '../ownKey.js';
2
2
  import { refuseBadDisableSkills, refuseDisableSkillsConflict } from '../disableSkillsWire.js';
3
3
  import { ATTACHMENTS_SPEC_KEYS, AttachmentsSpecRefused } from '../attachmentsWireCaps.js';
4
- import { retiredAttachmentsKeyOf } from '../taskRequestWords.js';
4
+ import { REASONING_EFFORT_WORDS, retiredAttachmentsKeyOf } from '../taskRequestWords.js';
5
+ import { THINKING_LATCH_SET_STATES } from '../thinkingLatchWords.js';
5
6
  import { msg, renderMessage } from '../i18n/translate.js';
6
7
  import { PROJECT_MEMORY_DISABLED } from '../projectMemory.js';
7
8
  import { frameFamiliesJudgementOf } from '../approvalFrameFamilies.js';
@@ -38,7 +39,7 @@ export const REQUEST_FIELD_MATRIX = [
38
39
  { field: 'inheritEnv', lanes: ['interactive', 'print'], live: true, why: '模型驱动的宿主 shell「继承多少用户环境」的**请求级声明**(server 7.106.0 提交体键,两词闭集 "all" / "scrub";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,续跑腿重放同判)。两条用户车道都是提交面、都可能在用户机器上跑 shell ⇒ 都能声明。本层两词原样透传、不按车道 / 部署预判(非 host 车道 × "all" 400、托管 × "all" 422 归上游判)。🔴 缺席 = 缺省清洗且清掉本会话上一条声明 ⇒ 要「本会话整份继承」就每个提交都带。🔴 只在服务端版本 ≥ 7.106.0 时发(老 server 对表外键整条 400),值由片段口 `inheritEnvDeclaration` 唯一铸出', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),没有模型驱动的 shell 去继承环境;带上它只会在不认这一键的老服务端上给这条提交多一种 400' },
39
40
  { field: 'requireExistingSession', lanes: ['interactive', 'print'], live: true, why: '「只续已有会话」的**请求级断言**(sdk `TaskRequest.requireExistingSession`):`true` 且会话不在 ⇒ 服务端在任何东西跑起来之前拒(会话不在 / 没给会话 id 两形),而**不是**静默新开一个同 id 的空会话;缺席 / `false` = 历史的「找不到就新开」。两条用户车道都有「续某个会话」的入口 ⇒ 都能声明。本层只认字面 `true`(`false` 与缺席同义、不发);非布尔坏值响亮拒(上游对它 400);`true` 而入参没有可续的会话 id ⇒ 装配期按名响亮拒,不留给上游。上游两形的拒绝经 `requireExistingSessionRefusalFromError` 归成一句', whyUtilityLane: '无座:side-channel 每一次提交都走壳另铸的**独立**会话 id(见 sessionId 那一行),从不续任何已有会话 ⇒「必须已存在」对它没有意义,带上去只会让每一次提交都被拒' },
40
41
  { field: 'disableSkills', lanes: ['interactive', 'print'], live: true, why: '「这一发一只技能都不装」的**请求级声明**(出厂技能、调用方给的技能、子代定义里的技能一个都不进引擎;其余工具照挂 —— 与 excludeAllTools 是两件事,两键同带各自生效)。两条用户车道都是提交面 ⇒ 都能声明。本层只认字面 true(false 与缺席同义、不发);非布尔坏值响亮拒(上游对它 400);与非空 skills 或任一非空 agents[].skills 同在 ⇒ 装配期按名响亮拒(上游对这一组合 400),兜底层落 agents 之后同判。🔴 只在能力位读数为真时发(老服务端对表外键整条 400),值由片段口 disableSkillsDeclaration 唯一铸出', whyUtilityLane: '无座:同 skills 一行 —— side-channel 是零工具面,装不装技能都无从谈起' },
41
- { field: 'finalVerification', lanes: ['print'], live: true, why: 'P1-1 终验:无人值守车道才需要引擎自证;交互 REPL 由人当场看结果。#106 裁 B(让位+告知)后交互面默认关', whyUtilityLane: '无座:终验让引擎再跑一轮自证,而 side-channel 要的是一次小提交的即时答案;「无人值守才需要引擎自证」这条理由在本车道上让位给「小预算优先」' },
42
+ { field: 'finalVerification', lanes: ['print'], live: true, why: 'P1-1 终验:无人值守车道才需要引擎自证;交互 REPL 由人当场看结果。裁定「让位+告知」后交互面默认关', whyUtilityLane: '无座:终验让引擎再跑一轮自证,而 side-channel 要的是一次小提交的即时答案;「无人值守才需要引擎自证」这条理由在本车道上让位给「小预算优先」' },
42
43
  { field: 'limits', lanes: ['print', 'utility'], live: true, why: 'P2-3-b:`-p` 的预算护栏(--max-* flag 族),交互 REPL 由人随时 Esc', whyUtilityLane: '有座:side-channel 必须带预算护栏(墙钟 / 输出 token / 轮数)—— 它同样没有人看着,而且没有后续回合可以中断' },
43
44
  { field: 'interactiveTools', lanes: ['print', 'utility'], live: true, why: '[909]B 件3:无人值守 stamp false,从 roster 源头灭掉 AskUserQuestion/plan 门。交互车道 stamp false 等于自废武功', whyUtilityLane: '有座:side-channel 绝不该弹交互问答 / plan 门(没有人在看这条提交)。它与 excludeAllTools 是**两条独立**的收紧声明、在这条车道上**并存**(本车道强制带卸载声明,见 `refuseUtilityWithoutToolUnload`;这一位是它旁边那条「连问都别问」的声明,不是它的降级替身)' },
44
45
  { field: 'oneShot', lanes: ['print', 'utility'], live: true, why: 'SDK TaskRequest.oneShot(server ≥7.12.0 消费):`-p` 提交是一次性的——没有后续回合接住异步后台通知,core 据此把「结束回合等通知」指引换成 block-wait;交互车道有后续回合,stamp true 就是谎报。老 worker 静默忽略(开集提交体),故发送侧不设能力位前置门(oneShotWireCaps 头注)', whyUtilityLane: '有座:side-channel 就是一次性提交 —— 没有后续回合接住异步后台通知,引擎据此把「结束回合等通知」换成 block-wait' },
@@ -191,6 +192,8 @@ const INPUT_KEY_ROUTES = {
191
192
  allowTools: row('allowTools'),
192
193
  hostSettings: { via: 'control' },
193
194
  approvalFrameFamiliesReading: { via: 'control' },
195
+ thinkingLatch: { via: 'control' },
196
+ reasoningFloor: { via: 'control' },
194
197
  settings: { via: 'settings' },
195
198
  };
196
199
  const HOST_TOP_KEY_ROUTES = {
@@ -202,6 +205,8 @@ const _hostTopKeysPin = [true];
202
205
  void _hostTopKeysPin;
203
206
  const _projectMemoryWordPin = [true, true];
204
207
  void _projectMemoryWordPin;
208
+ const _thinkingLatchWordPin = [true, true];
209
+ void _thinkingLatchWordPin;
205
210
  const HOST_TOP_ASK_AFTER = 'memoryCapture';
206
211
  const HOST_SETTINGS_SUBKEYS = Object.freeze(HOST_SUPPLIED_KEYS.filter((k) => k.startsWith('settings.')).map((k) => k.slice('settings.'.length)));
207
212
  const MINTED_SETTINGS_SUBKEYS = Object.freeze(['approvalFrameFamilies']);
@@ -699,6 +704,15 @@ const refuseBadProjectMemory = (v) => {
699
704
  '这一位是「这一次运行别读部署的项目上下文」的收紧声明,引擎是**单成员闭集**(没有第二个词:要照常读就不带这一键)且对坏值拒启。' +
700
705
  '本层若静默删键,请求会照发、项目指令文件会照常递给模型,而声明人不会知道自己的 opt-out 丢了。错误文本只报形状,不回显值。');
701
706
  };
707
+ const refuseBadThinkingLatch = (v) => {
708
+ if (v === undefined || v === null)
709
+ return;
710
+ if (typeof v === 'string' && THINKING_LATCH_SET_STATES.includes(v))
711
+ return;
712
+ throw new TypeError(`buildTaskRequest: thinkingLatch 只接受 ${THINKING_LATCH_SET_STATES.map((w) => `'${w}'`).join(' / ')} 或缺席(undefined/null = 未设)—— 收到 ${coarseShape(v)}。` +
713
+ '这一位是会话级思考开关的读数(从存储宿主的端口读:readThinkingLatch(port).state;未设就不带这一键):「关」被打字错误静默吞掉,' +
714
+ '这一发会照档位词思考而关掉开关的人不会知道。错误文本只报形状,不回显值。');
715
+ };
702
716
  const refuseBadRequireExistingSession = (v) => {
703
717
  if (v === undefined || v === null || v === true || v === false)
704
718
  return;
@@ -787,7 +801,7 @@ export function parseMaxCostUsd(text) {
787
801
  return { ok: false, why: 'not_positive' };
788
802
  return { ok: true, value };
789
803
  }
790
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills', 'projectMemory'];
804
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills', 'projectMemory', 'thinkingLatch'];
791
805
  const MATERIALIZE_AT_READ = Object.freeze({ allowTools: (v) => allowToolsSnapshot(v), userInvokedSkills: (v) => allowToolsSnapshot(v) });
792
806
  function dataFirstSnapshot(input, afterData) {
793
807
  let descs;
@@ -878,6 +892,7 @@ export function assembleTaskRequest(input, laneOrIdentity) {
878
892
  const approvalReachIn = src.approvalReach;
879
893
  const allowToolsIn = src.allowTools;
880
894
  const projectMemoryIn = src.projectMemory;
895
+ const thinkingLatchIn = src.thinkingLatch;
881
896
  const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
882
897
  const i = snapshotByName(src, INPUT_KEYS, {
883
898
  live,
@@ -892,6 +907,7 @@ export function assembleTaskRequest(input, laneOrIdentity) {
892
907
  approvalReach: approvalReachIn,
893
908
  allowTools: allowToolsIn,
894
909
  projectMemory: projectMemoryIn,
910
+ thinkingLatch: thinkingLatchIn,
895
911
  settings: settingsIn,
896
912
  });
897
913
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
@@ -921,6 +937,7 @@ export function assembleTaskRequest(input, laneOrIdentity) {
921
937
  refuseBadApprovalReach(approvalReachIn, 'buildTaskRequest');
922
938
  refuseBadAllowTools(allowToolsIn);
923
939
  refuseBadProjectMemory(projectMemoryIn);
940
+ refuseBadThinkingLatch(thinkingLatchIn);
924
941
  refuseBadUserInvokedSkills(i.userInvokedSkills);
925
942
  const rewindRoute = INPUT_KEY_ROUTES.rewind;
926
943
  const RETIRED_REWIND_FILES_KEY = 'rewindFiles';
@@ -1019,7 +1036,7 @@ export function assembleTaskRequest(input, laneOrIdentity) {
1019
1036
  return present(value);
1020
1037
  if (route.via === 'control' || route.via === 'settings')
1021
1038
  return false;
1022
- const cause = omissionCauseOf(standingOf(route), lane, live, origin);
1039
+ const cause = seatCauseOf(route, lane, live, origin);
1023
1040
  if (!inPlay(key, value))
1024
1041
  return false;
1025
1042
  if (cause === undefined)
@@ -1029,6 +1046,8 @@ export function assembleTaskRequest(input, laneOrIdentity) {
1029
1046
  };
1030
1047
  const gate = Object.create(null);
1031
1048
  const askInput = (key) => {
1049
+ if (key === THINKING_DEFERRED_ASK)
1050
+ return;
1032
1051
  const route = INPUT_KEY_ROUTES[key];
1033
1052
  if (route.via === 'settings') {
1034
1053
  for (const sub of SETTINGS_KEYS)
@@ -1047,8 +1066,16 @@ export function assembleTaskRequest(input, laneOrIdentity) {
1047
1066
  };
1048
1067
  for (const key of INPUT_KEYS.slice(hostTopAt))
1049
1068
  askInput(key);
1050
- const on = (key) => gate[key] === true;
1051
1069
  const onSetting = (sub) => gate[`settings.${sub}`] === true;
1070
+ const thinking = projectThinkingCore({
1071
+ lane,
1072
+ latch: thinkingLatchIn,
1073
+ strength: i.reasoningEffort,
1074
+ floor: i.reasoningFloor,
1075
+ ultracodeStamped: onSetting('ultracode') && s.ultracode === true,
1076
+ });
1077
+ gate[THINKING_DEFERRED_ASK] = carries(INPUT_KEY_ROUTES[THINKING_DEFERRED_ASK], thinking.word, THINKING_DEFERRED_ASK);
1078
+ const on = (key) => gate[key] === true;
1052
1079
  const settingsOut = {
1053
1080
  ...resolvedWire,
1054
1081
  ...(onSetting('webSearch') ? { webSearch: s.webSearch } : {}),
@@ -1063,7 +1090,7 @@ export function assembleTaskRequest(input, laneOrIdentity) {
1063
1090
  sessionId: i.sessionId,
1064
1091
  ...(on('scenario') ? { scenario: i.scenario } : {}),
1065
1092
  ...(on('systemPrompt') ? { systemPrompt: i.systemPrompt } : {}),
1066
- ...(on('reasoningEffort') ? { reasoningEffort: i.reasoningEffort } : {}),
1093
+ ...(on('reasoningEffort') ? { reasoningEffort: thinking.word } : {}),
1067
1094
  ...(on('model') ? { model: i.model } : {}),
1068
1095
  ...(on('images') ? { images: i.images } : {}),
1069
1096
  ...(on('userAttachments') ? { userAttachments: [...(i.userAttachments ?? [])] } : {}),
@@ -1107,11 +1134,100 @@ export function assembleTaskRequest(input, laneOrIdentity) {
1107
1134
  const familiesRoute = SETTINGS_KEY_ROUTES.approvalFrameFamilies;
1108
1135
  const familiesSeated = (familiesRoute.via === 'matrix' || familiesRoute.via === 'fixed') && omissionCauseOf(standingOf(familiesRoute), lane, live, origin) === undefined;
1109
1136
  const familiesWithheld = familiesSeated && approvalFamilies === undefined ? Object.freeze({ cause: familiesJudged === undefined || familiesJudged.ok ? 'reading_absent' : familiesJudged.cause }) : undefined;
1110
- return Object.freeze({ request: out, omitted: Object.freeze([...omitted, ...lateOmitted]), host, ...(familiesWithheld !== undefined ? { approvalFrameFamiliesWithheld: familiesWithheld } : {}) });
1137
+ return Object.freeze({
1138
+ request: out,
1139
+ omitted: Object.freeze([...omitted, ...lateOmitted]),
1140
+ host,
1141
+ ...(familiesWithheld !== undefined ? { approvalFrameFamiliesWithheld: familiesWithheld } : {}),
1142
+ thinking,
1143
+ });
1111
1144
  }
1112
1145
  export function buildTaskRequest(input, laneOrIdentity) {
1113
1146
  return assembleTaskRequest(input, laneOrIdentity).request;
1114
1147
  }
1148
+ const THINKING_DEFERRED_ASK = 'reasoningEffort';
1149
+ const FLOOR_WORDS = Object.freeze(REASONING_EFFORT_WORDS.filter((w) => w !== 'off'));
1150
+ const effortRank = (w) => (typeof w === 'string' ? REASONING_EFFORT_WORDS.indexOf(w) : -1);
1151
+ const seatCauseOf = (route, lane, live, origin) => omissionCauseOf(standingOf(route), lane, live, origin);
1152
+ const frozenProjection = (p) => Object.freeze(p);
1153
+ function projectThinkingCore(c) {
1154
+ const latch = c.latch === 'on' || c.latch === 'off' ? c.latch : 'unset';
1155
+ const ultracodeStamped = c.ultracodeStamped === true;
1156
+ const strength = c.strength;
1157
+ if (c.lane === 'utility')
1158
+ return frozenProjection({ word: strength, verdict: 'utility_lane', latch, ultracodeStamped });
1159
+ if (latch === 'off') {
1160
+ const overruled = present(strength) && strength !== 'off';
1161
+ return frozenProjection({ word: 'off', verdict: 'off', latch, ultracodeStamped, ...(overruled ? { strengthOverruled: strength } : {}) });
1162
+ }
1163
+ let base = strength;
1164
+ let overruledOff = false;
1165
+ if (latch === 'on' && base === 'off') {
1166
+ base = undefined;
1167
+ overruledOff = true;
1168
+ }
1169
+ let floorApplied = false;
1170
+ const floor = c.floor;
1171
+ if (typeof floor === 'string' && FLOOR_WORDS.includes(floor) && base !== 'off') {
1172
+ const baseRank = effortRank(base);
1173
+ if (!present(base) || (baseRank >= 0 && baseRank < effortRank(floor))) {
1174
+ base = floor;
1175
+ floorApplied = true;
1176
+ }
1177
+ }
1178
+ return frozenProjection({
1179
+ word: base,
1180
+ verdict: 'strength',
1181
+ latch,
1182
+ ultracodeStamped,
1183
+ ...(floorApplied ? { floorApplied: true } : {}),
1184
+ ...(overruledOff ? { strengthOverruled: 'off' } : {}),
1185
+ });
1186
+ }
1187
+ export function thinkingTurnProjection(input) {
1188
+ let submission;
1189
+ let live;
1190
+ let latch;
1191
+ let strength;
1192
+ let floor;
1193
+ let ultracode;
1194
+ try {
1195
+ if (typeof input === 'object' && input !== null) {
1196
+ submission = input.submission;
1197
+ live = input.live;
1198
+ latch = input.latch;
1199
+ strength = input.strength;
1200
+ floor = input.floor;
1201
+ ultracode = input.ultracode;
1202
+ }
1203
+ }
1204
+ catch {
1205
+ submission = live = latch = strength = floor = ultracode = undefined;
1206
+ }
1207
+ let lane;
1208
+ let origin;
1209
+ try {
1210
+ const axes = laneAxesOf(submission, 'seat');
1211
+ lane = axes.lane;
1212
+ origin = axes.origin;
1213
+ }
1214
+ catch {
1215
+ lane = undefined;
1216
+ origin = undefined;
1217
+ }
1218
+ const ultracodeRoute = SETTINGS_KEY_ROUTES.ultracode;
1219
+ let seated = false;
1220
+ try {
1221
+ seated =
1222
+ lane !== undefined &&
1223
+ (ultracodeRoute.via === 'matrix' || ultracodeRoute.via === 'fixed') &&
1224
+ seatCauseOf(ultracodeRoute, lane, live === true, origin) === undefined;
1225
+ }
1226
+ catch {
1227
+ seated = false;
1228
+ }
1229
+ return projectThinkingCore({ lane, latch, strength, floor, ultracodeStamped: seated && inPlay('settings.ultracode', ultracode) && ultracode === true });
1230
+ }
1115
1231
  export function applyLiveRequestDefaults(req, host) {
1116
1232
  const out = { ...req };
1117
1233
  if (out.cwd == null && host.cwd !== undefined)
package/dist/seam.js CHANGED
@@ -170,7 +170,7 @@ const CHROME_ARM_TABLE = {
170
170
  },
171
171
  text_segment_end: {
172
172
  required: true,
173
- duty: '🔴 引擎明报的 assistant 散文段边界 + **该段权威全文**(#323/core #447;server ≥7.75.3 起 content 经脱敏器、' +
173
+ duty: '🔴 引擎明报的 assistant 散文段边界 + **该段权威全文**(server ≥7.75.3 起 content 经脱敏器、' +
174
174
  'text_delta 仍逐字 ⇒ 两者**可以不相等**)。①`diverged` 在场(never false)⇒ 你按增量拼出来的那一段是过期的,' +
175
175
  '必须用 content **重渲该段**并以它为该段 committed 文本,不许保留增量拼文;②`committedPrefixDiverged` 在场' +
176
176
  '(never false)⇒ 这一段此前已 committed 上屏的正文**也**过期了,🔴 本包在这一形上**一个字节都不再交**(唯一不同:本段已提交行的归属说不出 —— 上游两行同 uuid,`committedUuids` 整键缺席 —— 时本包一个字节不动,还押着的原始那截在本事件之后照旧作为本段末行交出)' +
@@ -13,6 +13,7 @@ import type { SubagentContentEvent } from "./subagentContentStore.js";
13
13
  import type { DeviceEnrollTokenOutcome, DeviceListOutcome, DeviceRebindOutcome, DeviceRevokeOutcome, DeviceSessionBindingOutcome, DeviceTransport, DeviceUnarmed, DeviceUnreadable } from "./deviceRebind.js";
14
14
  import type { McpPanelView } from "./mcpPanel.js";
15
15
  import type { McpToolToggleKey } from "./mcpToolToggles.js";
16
+ import { type ThinkingLatchEvent, type ThinkingLatchRowWord } from "./thinkingLatchWords.js";
16
17
  export interface SeatValidationFailure {
17
18
  reason: string;
18
19
  field?: string;
@@ -47,6 +48,7 @@ export interface LocalSessionRecord {
47
48
  }>;
48
49
  parked?: boolean;
49
50
  pinned?: boolean;
51
+ thinkingLatch?: ThinkingLatchRowWord;
50
52
  }
51
53
  export declare const TOOL_PERMISSION_REQUEST_ID_DOMAINS: readonly ["live:", "durable:", "question:", "elicit:", "plan:"];
52
54
  export type ToolPermissionRequestIdDomain = (typeof TOOL_PERMISSION_REQUEST_ID_DOMAINS)[number];
@@ -190,7 +192,7 @@ export type SeatStreamReading = {
190
192
  };
191
193
  export declare function seatStreamDeltaMessage(event: SeatStreamDeltaSource, ids: SeatStreamDeltaIds): SeatStreamEventMessage | undefined;
192
194
  export declare function readSeatStreamDelta(message: object): SeatStreamReading;
193
- export declare const SEAT_CAPABILITY_NAMES: readonly ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles"];
195
+ export declare const SEAT_CAPABILITY_NAMES: readonly ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch"];
194
196
  export type SeatCapabilityName = (typeof SEAT_CAPABILITY_NAMES)[number];
195
197
  export interface SeatCapabilitiesState {
196
198
  pickFolder?: boolean;
@@ -201,6 +203,7 @@ export interface SeatCapabilitiesState {
201
203
  engineTarget?: boolean;
202
204
  onboard?: boolean;
203
205
  mcpToolToggles?: boolean;
206
+ thinkingLatch?: boolean;
204
207
  }
205
208
  export type SeatCapabilityReading = {
206
209
  readonly kind: "reported";
@@ -304,6 +307,10 @@ export interface SessionRowPatchRequest {
304
307
  pinned?: boolean;
305
308
  }
306
309
  export declare const SESSION_ROW_PATCH_REQUEST_KEYS: readonly ["sessionId", "title", "pinned"];
310
+ export interface SetThinkingLatchRequest {
311
+ readonly sessionId: string;
312
+ readonly event: ThinkingLatchEvent;
313
+ }
307
314
  export interface AttachmentUploadReceipt {
308
315
  id: string;
309
316
  name: string;
@@ -630,6 +637,7 @@ export interface LocalSessionsApi {
630
637
  readSessionMcpPanel(sessionId: string): Promise<McpPanelView | DeviceUnreadable | DeviceTransport | DeviceUnarmed>;
631
638
  listMcpToolToggles(): Promise<McpToolTogglesResult>;
632
639
  setMcpToolToggle(key: McpToolToggleKey, enabled: boolean): Promise<OkAck>;
640
+ setThinkingLatch(req: SetThinkingLatchRequest): Promise<OkAck>;
633
641
  onEvent(cb: (ev: LocalSessionEvent) => void): () => void;
634
642
  onToolPermissionRequest(cb: (req: ToolPermissionRequest) => void): () => void;
635
643
  sessionsStateStore: EipcStoreClient<SessionsState>;
@@ -671,14 +679,16 @@ export declare function checkSendMessageRequest(v: unknown): SeatValidation;
671
679
  export declare function isSendMessageRequest(v: unknown): v is SendMessageRequest;
672
680
  export declare const checkSessionRowPatchRequest: (v: unknown) => SeatValidation;
673
681
  export declare function isSessionRowPatchRequest(v: unknown): v is SessionRowPatchRequest;
682
+ export declare const checkSetThinkingLatchRequest: (v: unknown) => SeatValidation;
683
+ export declare function isSetThinkingLatchRequest(v: unknown): v is SetThinkingLatchRequest;
674
684
  export declare function isSendMessageAck(v: unknown): v is SendMessageAck;
675
685
  export declare const LOCAL_SESSIONS_SPEC: EipcServiceSpec;
676
- export declare const SEAT_METHOD_NAMES: readonly ["start", "sendMessage", "interrupt", "stop", "respondToToolPermission", "cancelQueuedMessage", "reorderQueuedMessage", "promoteQueuedMessage", "setFocusedSession", "setModel", "getTranscript", "searchSessions", "uploadAttachment", "listModels", "applyModelConfig", "pickFolder", "listRecentFolders", "noteRecentFolder", "patchSession", "listDevices", "readSessionBinding", "rebindSession", "issueEnrollToken", "revokeDevice", "setEngineMode", "startCloudLogin", "cancelCloudLogin", "connectCloudEngine", "configureCloudEngine", "clearCloudEngine", "listOnboardProviders", "probeOnboardEntry", "applyOnboardEntry", "readSessionMcpPanel", "listMcpToolToggles", "setMcpToolToggle", "onEvent", "onToolPermissionRequest"];
686
+ export declare const SEAT_METHOD_NAMES: readonly ["start", "sendMessage", "interrupt", "stop", "respondToToolPermission", "cancelQueuedMessage", "reorderQueuedMessage", "promoteQueuedMessage", "setFocusedSession", "setModel", "getTranscript", "searchSessions", "uploadAttachment", "listModels", "applyModelConfig", "pickFolder", "listRecentFolders", "noteRecentFolder", "patchSession", "listDevices", "readSessionBinding", "rebindSession", "issueEnrollToken", "revokeDevice", "setEngineMode", "startCloudLogin", "cancelCloudLogin", "connectCloudEngine", "configureCloudEngine", "clearCloudEngine", "listOnboardProviders", "probeOnboardEntry", "applyOnboardEntry", "readSessionMcpPanel", "listMcpToolToggles", "setMcpToolToggle", "setThinkingLatch", "onEvent", "onToolPermissionRequest"];
677
687
  export declare const SEAT_STORE_PROPS: readonly ["sessionsStateStore", "focusedSessionStateStore", "settingsStateStore", "seatCapabilitiesStateStore", "engineTargetStateStore"];
678
688
  export declare const SEAT_EVENT_METHOD_NAMES: readonly ["onEvent", "onToolPermissionRequest"];
679
689
  export declare const LOCAL_SESSION_EVENT_KEYS: readonly ["type", "sessionId", "message", "error", "errorCategory", "code", "session", "userMessageUuid", "requestId", "outcome", "parked", "chrome", "_sema_turnFailure", "subagentContent"];
680
690
  export declare const TOOL_PERMISSION_REQUEST_KEYS: readonly ["requestId", "sessionId", "toolName", "toolCallId", "input", "message", "toctouBinding", "createdAtMs", "governanceForced", "delegation", "mandated", "mandate", "origin", "ruleOffersAbsence", "askClass", "classes", "sessionRow"];
681
- export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned"];
691
+ export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"];
682
692
  export declare const SEND_MESSAGE_REQUEST_KEYS: readonly ["sessionId", "text", "model", "userMessageUuid", "permissionMode", "attachmentIds", "reasoningEffort", "retainBackgroundProcesses", "forwardSubagentEvents", "retainSubagentSessions", "settings", "scenario", "rewind"];
683
693
  export declare const SEAT_SEND_MESSAGE_KEY_ORIGINS: {
684
694
  readonly sessionId: {
@@ -728,7 +738,7 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
728
738
  readonly LocalSessionRecord: {
729
739
  readonly guard: "isLocalSessionRecord";
730
740
  readonly check: (v: unknown) => SeatValidation;
731
- readonly validated: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned"];
741
+ readonly validated: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"];
732
742
  readonly unchecked: readonly [];
733
743
  };
734
744
  readonly ToolPermissionRequest: {
@@ -808,6 +818,12 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
808
818
  readonly validated: readonly ["sessionId", "title", "pinned"];
809
819
  readonly unchecked: readonly [];
810
820
  };
821
+ readonly SetThinkingLatchRequest: {
822
+ readonly guard: "isSetThinkingLatchRequest";
823
+ readonly check: (v: unknown) => SeatValidation;
824
+ readonly validated: readonly ["sessionId", "event"];
825
+ readonly unchecked: readonly [];
826
+ };
811
827
  readonly ApplyModelConfigResult: {
812
828
  readonly check: (v: unknown) => SeatValidation;
813
829
  readonly validated: readonly ["ok", "requiresRestart", "reason", "detail"];
@@ -861,7 +877,7 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
861
877
  };
862
878
  readonly SeatCapabilitiesState: {
863
879
  readonly check: (v: unknown) => SeatValidation;
864
- readonly validated: readonly ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles"];
880
+ readonly validated: readonly ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch"];
865
881
  readonly unchecked: readonly [];
866
882
  };
867
883
  readonly RecentFoldersResult: {
@@ -907,7 +923,7 @@ export declare const SEAT_VALIDATOR_COVERAGE_EXEMPT: readonly [...{
907
923
  }[], {
908
924
  readonly label: "PickFolderResult";
909
925
  readonly check: (v: unknown) => SeatValidation;
910
- readonly why: "载荷是**串或 null**(选中的路径 / 用户取消,CC-469),没有键面 ⇒ `keyof` 覆盖表在它身上无从谈起。判别力由 run-seat-wave2-test.mjs ST1-1 承担:认 null 与非空串、拒空串 / 数 / undefined / 对象 / 布尔五形。";
926
+ readonly why: "载荷是**串或 null**(选中的路径 / 用户取消,CC-469),没有键面 ⇒ `keyof` 覆盖表在它身上无从谈起。判别力由对应的座位契约测试套承担:认 null 与非空串、拒空串 / 数 / undefined / 对象 / 布尔五形。";
911
927
  }, {
912
928
  readonly label: "BooleanAck";
913
929
  readonly check: (v: unknown) => SeatValidation;
@@ -5,6 +5,7 @@ import { SETTINGS_MODEL_API_WORDS } from "./generated/settingsSchemaTables.js";
5
5
  import { PROBE_VERDICTS } from "./modelCapabilityProbe.js";
6
6
  import { guardedRead, ownDataField, ownDataRefusalKeyOf, ownSendableField, readOwnDataValue } from "./ownKey.js";
7
7
  import { MCP_LIVENESS_STATES } from "./mcpLiveness.js";
8
+ import { THINKING_LATCH_EVENTS, THINKING_LATCH_ROW_WORDS } from "./thinkingLatchWords.js";
8
9
  const bad = (reason, field) => field === undefined ? { reason } : { reason, field };
9
10
  const nested = (prefix, verdict) => {
10
11
  const path = verdict.field === undefined
@@ -158,6 +159,7 @@ const oneOfRule = (label, words) => ruleOf(label, (v) => typeof v === "string" &
158
159
  const R_STR = ruleOf("string", (v) => typeof v === "string");
159
160
  const R_NONEMPTY = ruleOf("non-empty string", (v) => typeof v === "string" && v.length > 0);
160
161
  const R_BOOL = ruleOf("boolean", (v) => typeof v === "boolean");
162
+ const R_THINKING_LATCH_ROW = oneOfRule("latch word", THINKING_LATCH_ROW_WORDS);
161
163
  const R_TRUE = ruleOf("true", (v) => v === true);
162
164
  const R_NUM = ruleOf("finite number", (v) => typeof v === "number" && Number.isFinite(v));
163
165
  const R_INT0 = ruleOf("non-negative integer", (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0);
@@ -354,7 +356,7 @@ export function readSeatStreamDelta(message) {
354
356
  return Object.freeze({ kind: "malformed" });
355
357
  }
356
358
  }
357
- export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles"]);
359
+ export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch"]);
358
360
  const notReported = () => Object.freeze({ kind: "not_reported" });
359
361
  const malformedReading = () => Object.freeze({ kind: "malformed" });
360
362
  const reported = (available) => Object.freeze({ kind: "reported", available });
@@ -430,6 +432,7 @@ const SEAT_CAPABILITY_PHRASES = Object.freeze({
430
432
  engineTarget: Object.freeze({ action: "Switching engines", ability: "switch engines", support: "engine-switching support" }),
431
433
  onboard: Object.freeze({ action: "Model setup", ability: "add models", support: "model-setup support" }),
432
434
  mcpToolToggles: Object.freeze({ action: "Per-tool switching", ability: "toggle tools", support: "per-tool switches" }),
435
+ thinkingLatch: Object.freeze({ action: "Switching thinking", ability: "switch thinking", support: "thinking-switch support" }),
433
436
  });
434
437
  export function seatCapabilityUnavailableDetail(name, reading) {
435
438
  const phrase = Object.hasOwn(SEAT_CAPABILITY_PHRASES, name)
@@ -556,6 +559,7 @@ export const SEAT_TURN_SETTINGS_ORIGINS = Object.freeze({
556
559
  export const SEAT_REWIND_INTENT_KEYS = ["mode", "targetMessageUuid"];
557
560
  export const ATTACHMENT_UPLOAD_REQUEST_KEYS = ["name", "mimeType", "bytes"];
558
561
  export const SESSION_ROW_PATCH_REQUEST_KEYS = Object.freeze(["sessionId", "title", "pinned"]);
562
+ const SET_THINKING_LATCH_REQUEST_KEYS = Object.freeze(["sessionId", "event"]);
559
563
  export const SESSION_SEARCH_RESULT_KEYS = ["sessionId", "title", "matches"];
560
564
  export const GET_TRANSCRIPT_RESULT_KEYS = ["sessionId", "messages"];
561
565
  const DEVICE_REFUSED_SPEC = { status: reqF(R_INT0), errorCode: optF(R_STR), message: optF(R_STR) };
@@ -971,6 +975,12 @@ const checkLocalSessionRecord = seatCheck((s) => {
971
975
  if (!isAbsent(pinned) && typeof pinned !== "boolean") {
972
976
  return bad("pinned must be a boolean when present (absent = not pinned or unknown)", "pinned");
973
977
  }
978
+ const latch = own(s, "thinkingLatch");
979
+ if (!isAbsent(latch)) {
980
+ const verdict = R_THINKING_LATCH_ROW.test(latch, "thinkingLatch");
981
+ if (verdict !== true)
982
+ return verdict;
983
+ }
974
984
  return true;
975
985
  });
976
986
  export function isLocalSessionRecord(s) {
@@ -1438,6 +1448,14 @@ export const checkSessionRowPatchRequest = seatCheck((v) => {
1438
1448
  export function isSessionRowPatchRequest(v) {
1439
1449
  return checkSessionRowPatchRequest(v) === true;
1440
1450
  }
1451
+ const checkSetThinkingLatchShape = seatShapeCheck(shapeRule("latch request", {
1452
+ sessionId: reqF(R_NONEMPTY),
1453
+ event: reqF(oneOfRule("latch event", THINKING_LATCH_EVENTS)),
1454
+ }));
1455
+ export const checkSetThinkingLatchRequest = seatCheck((v) => isPlainRecord(v) ? checkSetThinkingLatchShape(v) : bad("setThinkingLatch request is not a plain object"));
1456
+ export function isSetThinkingLatchRequest(v) {
1457
+ return checkSetThinkingLatchRequest(v) === true;
1458
+ }
1441
1459
  const checkSendMessageAck = seatCheck((v) => {
1442
1460
  if (!isPlainRecord(v))
1443
1461
  return bad("sendMessage ack is not a plain object");
@@ -1609,6 +1627,7 @@ export const LOCAL_SESSIONS_SPEC = {
1609
1627
  readSessionMcpPanel: { kind: "invoke", check: checkSessionMcpPanelResult },
1610
1628
  listMcpToolToggles: { kind: "invoke", check: checkMcpToolTogglesResult },
1611
1629
  setMcpToolToggle: { kind: "invoke", check: checkOkAck },
1630
+ setThinkingLatch: { kind: "invoke", check: checkOkAck },
1612
1631
  onEvent: { kind: "event", check: checkLocalSessionEvent },
1613
1632
  onToolPermissionRequest: { kind: "event", check: checkToolPermissionRequest },
1614
1633
  },
@@ -1656,6 +1675,7 @@ export const SEAT_METHOD_NAMES = [
1656
1675
  "readSessionMcpPanel",
1657
1676
  "listMcpToolToggles",
1658
1677
  "setMcpToolToggle",
1678
+ "setThinkingLatch",
1659
1679
  "onEvent",
1660
1680
  "onToolPermissionRequest",
1661
1681
  ];
@@ -1714,6 +1734,7 @@ export const LOCAL_SESSION_RECORD_KEYS = [
1714
1734
  "queuedMessages",
1715
1735
  "parked",
1716
1736
  "pinned",
1737
+ "thinkingLatch",
1717
1738
  ];
1718
1739
  export const SEND_MESSAGE_REQUEST_KEYS = [
1719
1740
  "sessionId",
@@ -1758,7 +1779,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1758
1779
  LocalSessionRecord: {
1759
1780
  guard: "isLocalSessionRecord",
1760
1781
  check: checkLocalSessionRecord,
1761
- validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned"],
1782
+ validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"],
1762
1783
  unchecked: [],
1763
1784
  },
1764
1785
  ToolPermissionRequest: {
@@ -1882,6 +1903,12 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1882
1903
  validated: ["sessionId", "title", "pinned"],
1883
1904
  unchecked: [],
1884
1905
  },
1906
+ SetThinkingLatchRequest: {
1907
+ guard: "isSetThinkingLatchRequest",
1908
+ check: checkSetThinkingLatchRequest,
1909
+ validated: ["sessionId", "event"],
1910
+ unchecked: [],
1911
+ },
1885
1912
  ApplyModelConfigResult: {
1886
1913
  check: checkApplyModelConfigResult,
1887
1914
  validated: ["ok", "requiresRestart", "reason", "detail"],
@@ -1935,7 +1962,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1935
1962
  },
1936
1963
  SeatCapabilitiesState: {
1937
1964
  check: checkSeatCapabilitiesState,
1938
- validated: ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles"],
1965
+ validated: ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch"],
1939
1966
  unchecked: [],
1940
1967
  },
1941
1968
  RecentFoldersResult: {
@@ -2006,7 +2033,7 @@ export const SEAT_VALIDATOR_COVERAGE_EXEMPT = [
2006
2033
  {
2007
2034
  label: "PickFolderResult",
2008
2035
  check: checkPickFolderResult,
2009
- why: "载荷是**串或 null**(选中的路径 / 用户取消,CC-469),没有键面 ⇒ `keyof` 覆盖表在它身上无从谈起。判别力由 run-seat-wave2-test.mjs ST1-1 承担:认 null 与非空串、拒空串 / 数 / undefined / 对象 / 布尔五形。",
2036
+ why: "载荷是**串或 null**(选中的路径 / 用户取消,CC-469),没有键面 ⇒ `keyof` 覆盖表在它身上无从谈起。判别力由对应的座位契约测试套承担:认 null 与非空串、拒空串 / 数 / undefined / 对象 / 布尔五形。",
2010
2037
  },
2011
2038
  {
2012
2039
  label: "BooleanAck",
@@ -2017,10 +2044,10 @@ export const SEAT_VALIDATOR_COVERAGE_EXEMPT = [
2017
2044
  label: "LaneProof",
2018
2045
  check: (v) => checkLaneProof(v, "laneProof"),
2019
2046
  why: "判别式联合:必填键面随 `lane` 臂而变(subagent 要 parentToolCallId、workflow 要 via+key),而覆盖表的方法是「一个最小合法体 + 逐键投毒」—— 那套方法在它身上没有判别力(main 臂对另外两臂的伴随位本来就不该拒,投毒过了不代表漏校)。" +
2020
- "🔴 FIX④(2026-08-07 订正):本条此前自称判别力由「F 段**逐臂**投毒(空对象 / 非对象 / **未登记车道词**)」承担 —— 全树实扫,那三样里只有**空对象**与**非对象**真的存在(run-seat-contract-keys-test.mjs F 段)," +
2047
+ "🔴 (2026-08-07 订正)本条此前自称判别力由「**逐臂**投毒(空对象 / 非对象 / **未登记车道词**)」承担 —— 全树实扫,那三样里只有**空对象**与**非对象**真的存在(座位契约键测试套的逐臂投毒段)," +
2021
2048
  "「未登记车道词」实际只钉了 `lane: 7`(非串,证明不了闭集成员判定在场),「逐臂」(subagent 缺 parentToolCallId / workflow 缺 via+key)则是**零覆盖**。也就是说这条豁免曾经拿三件不存在的判据给自己背书。" +
2022
2049
  "现势(判据锚在真存在的东西上):① `LANE_PROOF_LANE_TUPLE` ⇄ seam.ts 车道联合的双向 `Covers`(本文件 `_seatContractPins`,加车道不跟 ⇒ 编译红);" +
2023
- "② run-client-core-pure-test.mjs **FIX7 段 ④** —— 未登记车道词 / subagent 缺伴随位 / workflow 缺 via 或 key 三条投毒 + 两条逐臂正控 + 一条「main 臂不被过紧误拒」负控,全部直接喂本条的 `check`。",
2050
+ "② 本包纯测试套的投毒段 —— 未登记车道词 / subagent 缺伴随位 / workflow 缺 via 或 key 三条投毒 + 两条逐臂正控 + 一条「main 臂不被过紧误拒」负控,全部直接喂本条的 `check`。",
2024
2051
  },
2025
2052
  ];
2026
2053
  const _seatContractPins = [
@@ -2050,6 +2077,8 @@ const _seatContractPins = [
2050
2077
  true, true, true,
2051
2078
  true,
2052
2079
  true, true, true,
2080
+ true, true, true, true, true, true,
2081
+ true, true, true,
2053
2082
  true, true,
2054
2083
  ];
2055
2084
  void _seatContractPins;