@sema-agent/client-core 0.100.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 (358) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/README.md +172 -120
  3. package/dist/adapt/arms.js +51 -16
  4. package/dist/adapt/textStream.js +7 -4
  5. package/dist/adapt/toolCards.d.ts +2 -1
  6. package/dist/adapt/toolCards.js +28 -13
  7. package/dist/adapt.d.ts +1 -1
  8. package/dist/adapt.js +8 -2
  9. package/dist/adapter/activeRunSelfHeal.d.ts +24 -2
  10. package/dist/adapter/activeRunSelfHeal.js +108 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +38 -79
  12. package/dist/adapter/downstream/terminalToSdkResult.d.ts +60 -1
  13. package/dist/adapter/downstream/terminalToSdkResult.js +209 -39
  14. package/dist/adapter/downstream/wiringManifestView.d.ts +1 -0
  15. package/dist/adapter/downstream/wiringManifestView.js +138 -0
  16. package/dist/adapter/runStream.d.ts +4 -4
  17. package/dist/adapter/runStream.js +158 -43
  18. package/dist/agentsWireCaps.d.ts +2 -7
  19. package/dist/agentsWireCaps.js +1 -2
  20. package/dist/approvalDecidedByCapability.js +2 -2
  21. package/dist/approvalFrameFamilies.d.ts +33 -0
  22. package/dist/approvalFrameFamilies.js +120 -0
  23. package/dist/approvalsStreamLiveCapability.js +2 -2
  24. package/dist/attachmentsWireCaps.js +41 -13
  25. package/dist/callerCwdCapability.js +2 -2
  26. package/dist/centerWiringCapability.js +2 -2
  27. package/dist/clockReading.d.ts +2 -0
  28. package/dist/clockReading.js +4 -0
  29. package/dist/cloudConfigWireCaps.d.ts +19 -5
  30. package/dist/cloudConfigWireCaps.js +193 -56
  31. package/dist/compensations.js +1 -1
  32. package/dist/coreValuePorts.d.ts +12 -2
  33. package/dist/coreValuePorts.js +9 -0
  34. package/dist/decideFailureNote.js +5 -3
  35. package/dist/decideReceipt.js +2 -2
  36. package/dist/deviceExecutorManagementCapability.js +2 -2
  37. package/dist/deviceFlowPolling.js +1 -1
  38. package/dist/disableSkillsWire.d.ts +1 -1
  39. package/dist/disableSkillsWire.js +4 -3
  40. package/dist/displayUntrusted.d.ts +9 -4
  41. package/dist/displayUntrusted.js +834 -104
  42. package/dist/engineCapReader.js +1 -1
  43. package/dist/engineConfigDrift.js +1 -1
  44. package/dist/engineErrorCodes.d.ts +14 -5
  45. package/dist/engineErrorCodes.js +27 -6
  46. package/dist/engineGenerationConvergence.d.ts +39 -0
  47. package/dist/engineGenerationConvergence.js +95 -0
  48. package/dist/engineGenerationPort.d.ts +61 -0
  49. package/dist/engineGenerationPort.js +144 -0
  50. package/dist/engineHealthFacts.d.ts +23 -0
  51. package/dist/engineHealthFacts.js +92 -0
  52. package/dist/engineIdentity.d.ts +4 -0
  53. package/dist/engineIdentity.js +11 -0
  54. package/dist/engineNoticeCodes.d.ts +2 -5
  55. package/dist/engineRowRef.js +4 -0
  56. package/dist/engineSessionMessages.js +2 -2
  57. package/dist/engineToolDirectory.js +2 -2
  58. package/dist/engineWireFor.d.ts +99 -0
  59. package/dist/engineWireFor.js +248 -6
  60. package/dist/engineWireTarget.d.ts +1 -0
  61. package/dist/engineWireTarget.js +8 -0
  62. package/dist/env/uiLanguage.js +1 -1
  63. package/dist/errorSupplementRoles.d.ts +3 -0
  64. package/dist/errorSupplementRoles.js +37 -0
  65. package/dist/executionLaneCapability.js +2 -2
  66. package/dist/fileHistoryCaptureCapability.js +3 -3
  67. package/dist/finalVerifyWire.d.ts +6 -3
  68. package/dist/finalVerifyWire.js +6 -23
  69. package/dist/flagInput.d.ts +4 -0
  70. package/dist/flagInput.js +24 -0
  71. package/dist/fleet/fleetLedger.js +5 -2
  72. package/dist/fleet/fleetProjection.d.ts +1 -0
  73. package/dist/fleet/fleetProjection.js +16 -3
  74. package/dist/gateVocabulary.js +3 -0
  75. package/dist/generated/engineFactTables.d.ts +7 -3
  76. package/dist/generated/engineFactTables.js +52 -0
  77. package/dist/generated/engineNoticeTables.js +12 -0
  78. package/dist/generated/settingsSchemaActiveTiers.d.ts +1 -0
  79. package/dist/generated/settingsSchemaActiveTiers.js +14 -0
  80. package/dist/generated/settingsSchemaCallerTextEcho.d.ts +1 -0
  81. package/dist/generated/settingsSchemaCallerTextEcho.js +8 -0
  82. package/dist/generated/settingsSchemaControlAndBidi.d.ts +1 -0
  83. package/dist/generated/settingsSchemaControlAndBidi.js +8 -0
  84. package/dist/generated/settingsSchemaDefaultModel.d.ts +11 -0
  85. package/dist/generated/settingsSchemaDefaultModel.js +25 -0
  86. package/dist/generated/settingsSchemaFns.d.ts +54 -0
  87. package/dist/generated/settingsSchemaFns.js +26 -1
  88. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
  89. package/dist/generated/settingsSchemaMcpRequestEntry.js +1 -7
  90. package/dist/generated/settingsSchemaSafetyMergeSpec.d.ts +1 -0
  91. package/dist/generated/settingsSchemaSafetyMergeSpec.js +95 -0
  92. package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -0
  93. package/dist/generated/settingsSchemaSettingsSources.js +58 -0
  94. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  95. package/dist/generated/settingsSchemaTables.js +13 -0
  96. package/dist/generated/settingsSchemaUrlPattern.d.ts +1 -0
  97. package/dist/generated/settingsSchemaUrlPattern.js +183 -0
  98. package/dist/headlessPermissionModeWire.d.ts +18 -2
  99. package/dist/headlessPermissionModeWire.js +30 -19
  100. package/dist/hitl/allowRuleShadow.js +7 -5
  101. package/dist/hitl/approvalCardReading.d.ts +3 -0
  102. package/dist/hitl/approvalCardReading.js +17 -0
  103. package/dist/hitl/approvalCompleteParked.js +1 -1
  104. package/dist/hitl/approvalDelivery.d.ts +87 -0
  105. package/dist/hitl/approvalDelivery.js +193 -0
  106. package/dist/hitl/approvalResolution.d.ts +11 -0
  107. package/dist/hitl/approvalResolution.js +50 -11
  108. package/dist/hitl/askClassPair.d.ts +6 -0
  109. package/dist/hitl/askClassPair.js +30 -3
  110. package/dist/hitl/askParkAutoReopenBlock.d.ts +30 -0
  111. package/dist/hitl/askParkAutoReopenBlock.js +117 -0
  112. package/dist/hitl/askParkRowRouting.js +4 -1
  113. package/dist/hitl/attributionLadder.d.ts +42 -3
  114. package/dist/hitl/attributionLadder.js +79 -8
  115. package/dist/hitl/automaticAnswerVerdict.d.ts +6 -1
  116. package/dist/hitl/automaticAnswerVerdict.js +46 -11
  117. package/dist/hitl/crashConverged.js +2 -2
  118. package/dist/hitl/decisionConflict.d.ts +6 -2
  119. package/dist/hitl/decisionConflict.js +24 -9
  120. package/dist/hitl/decisionRefusal.d.ts +20 -10
  121. package/dist/hitl/decisionRefusal.js +99 -54
  122. package/dist/hitl/decisionSend.d.ts +3 -0
  123. package/dist/hitl/decisionSend.js +87 -103
  124. package/dist/hitl/frameRouter.d.ts +1 -0
  125. package/dist/hitl/frameRouter.js +9 -2
  126. package/dist/hitl/hitlBridge.d.ts +18 -6
  127. package/dist/hitl/hitlBridge.js +41 -37
  128. package/dist/hitl/livePendingAsk.js +4 -3
  129. package/dist/hitl/noAllowCause.js +1 -1
  130. package/dist/hitl/parkResolver.js +3 -2
  131. package/dist/hitl/persistedRulesWire.js +1 -0
  132. package/dist/hitl/planReviewWire.d.ts +4 -0
  133. package/dist/hitl/planReviewWire.js +59 -30
  134. package/dist/hitl/resumeRunningCard.d.ts +3 -2
  135. package/dist/hitl/resumeRunningCard.js +17 -11
  136. package/dist/hitl/sessionPolicyDeliverable.d.ts +5 -2
  137. package/dist/hitl/sessionPolicyDeliverable.js +62 -29
  138. package/dist/hitl/sessionPolicyWire.d.ts +13 -3
  139. package/dist/hitl/sessionPolicyWire.js +81 -16
  140. package/dist/hitl/standingRows.js +1 -1
  141. package/dist/hitl/toolApprovalWire.d.ts +9 -10
  142. package/dist/hitl/toolApprovalWire.js +72 -54
  143. package/dist/hookMatcherVerdict.d.ts +2 -0
  144. package/dist/hookMatcherVerdict.js +12 -0
  145. package/dist/hooksCompactTransition.d.ts +19 -0
  146. package/dist/hooksCompactTransition.js +41 -0
  147. package/dist/hooksWireCaps.d.ts +1 -1
  148. package/dist/hooksWireCaps.js +202 -203
  149. package/dist/host.d.ts +1 -14
  150. package/dist/host.js +0 -8
  151. package/dist/hostCallbackGuard.d.ts +1 -0
  152. package/dist/hostCallbackGuard.js +1 -1
  153. package/dist/hostSettingsSource.d.ts +172 -0
  154. package/dist/hostSettingsSource.js +675 -0
  155. package/dist/hostTurnSettings.d.ts +73 -17
  156. package/dist/hostTurnSettings.js +594 -97
  157. package/dist/httpHookPolicy.d.ts +37 -0
  158. package/dist/httpHookPolicy.js +176 -0
  159. package/dist/humanTurn.d.ts +2 -1
  160. package/dist/humanTurn.js +37 -7
  161. package/dist/humanTurnRead.js +4 -3
  162. package/dist/humanTurnSentences.d.ts +10 -0
  163. package/dist/humanTurnSentences.js +74 -0
  164. package/dist/i18n/messages.en.d.ts +222 -2
  165. package/dist/i18n/messages.en.js +225 -5
  166. package/dist/i18n/messages.zh.js +237 -5
  167. package/dist/i18n/translate.js +3 -3
  168. package/dist/index.d.ts +77 -15
  169. package/dist/index.js +50 -13
  170. package/dist/inheritEnvCapability.d.ts +3 -1
  171. package/dist/inheritEnvCapability.js +31 -17
  172. package/dist/inheritEnvWire.d.ts +2 -1
  173. package/dist/inheritEnvWire.js +11 -5
  174. package/dist/interactiveHalt.d.ts +3 -1
  175. package/dist/interactiveHalt.js +2 -0
  176. package/dist/interactiveInterrupt.d.ts +2 -0
  177. package/dist/interactiveInterrupt.js +15 -0
  178. package/dist/interactiveToolsWire.d.ts +4 -2
  179. package/dist/interactiveToolsWire.js +5 -5
  180. package/dist/jsonFaithful.d.ts +20 -0
  181. package/dist/jsonFaithful.js +152 -0
  182. package/dist/limitsWire.d.ts +7 -2
  183. package/dist/limitsWire.js +19 -13
  184. package/dist/liveInitToolFace.d.ts +6 -1
  185. package/dist/liveInitToolFace.js +5 -3
  186. package/dist/liveModelCatalog.d.ts +22 -0
  187. package/dist/liveModelCatalog.js +132 -18
  188. package/dist/mcpPanel.js +1 -1
  189. package/dist/mcpProbeCapability.js +2 -2
  190. package/dist/mcpStatusSupersetKeys.d.ts +24 -0
  191. package/dist/mcpStatusSupersetKeys.js +45 -0
  192. package/dist/mcpToolToggles.js +2 -1
  193. package/dist/mcpWireCaps.d.ts +39 -1
  194. package/dist/mcpWireCaps.js +254 -50
  195. package/dist/memoryComplianceCapability.js +2 -2
  196. package/dist/memoryOriginCapability.js +2 -2
  197. package/dist/model/catalogDefault.d.ts +24 -0
  198. package/dist/model/catalogDefault.js +4 -0
  199. package/dist/model/liveCatalogShape.js +5 -4
  200. package/dist/model/modelsPolicy.js +4 -3
  201. package/dist/model/onboardJudgement.d.ts +1 -0
  202. package/dist/model/onboardJudgement.js +1 -1
  203. package/dist/model/subagentModelWord.js +2 -2
  204. package/dist/modelCapabilityProbe.js +6 -6
  205. package/dist/modelIdentityResolvability.d.ts +10 -1
  206. package/dist/modelIdentityResolvability.js +62 -39
  207. package/dist/modsFace.d.ts +8 -0
  208. package/dist/modsFace.js +59 -0
  209. package/dist/modsKernelIdentity.d.ts +10 -0
  210. package/dist/modsKernelIdentity.js +103 -0
  211. package/dist/modsKernelTransit.d.ts +8 -0
  212. package/dist/modsKernelTransit.js +6 -0
  213. package/dist/notifications.d.ts +30 -12
  214. package/dist/notifications.js +241 -53
  215. package/dist/outputStyleSeatCapability.js +2 -2
  216. package/dist/ownKey.d.ts +12 -0
  217. package/dist/ownKey.js +179 -3
  218. package/dist/panelReduce.js +1 -1
  219. package/dist/peerFrames.d.ts +1 -1
  220. package/dist/peerFrames.js +6 -2
  221. package/dist/peerLaneCapability.js +2 -2
  222. package/dist/permissionModesCapability.js +4 -4
  223. package/dist/permissionRuleIssue.js +2 -0
  224. package/dist/permissionRulesWriteCapability.js +2 -2
  225. package/dist/permissionsAllowGrantCapability.js +2 -2
  226. package/dist/permissionsDeclaration.d.ts +43 -4
  227. package/dist/permissionsDeclaration.js +102 -35
  228. package/dist/pluginHooksWire.d.ts +20 -4
  229. package/dist/pluginHooksWire.js +92 -53
  230. package/dist/prepareRefusal.d.ts +3 -2
  231. package/dist/prepareRefusal.js +27 -9
  232. package/dist/printInitToolFace.js +2 -2
  233. package/dist/printModelStatus.js +1 -1
  234. package/dist/printToolResultFrame.js +1 -1
  235. package/dist/projectMemory.d.ts +32 -0
  236. package/dist/projectMemory.js +70 -0
  237. package/dist/promptBlockedCopy.d.ts +2 -1
  238. package/dist/promptBlockedCopy.js +2 -1
  239. package/dist/promptDelivery.d.ts +23 -3
  240. package/dist/promptDelivery.js +169 -30
  241. package/dist/providerRejection.d.ts +19 -1
  242. package/dist/providerRejection.js +75 -4
  243. package/dist/queuedDelivery.d.ts +48 -0
  244. package/dist/queuedDelivery.js +233 -0
  245. package/dist/reattachWindow.d.ts +94 -0
  246. package/dist/reattachWindow.js +127 -0
  247. package/dist/request/printNotification.d.ts +1 -1
  248. package/dist/request/printNotification.js +6 -2
  249. package/dist/request/submissionIdentity.d.ts +37 -0
  250. package/dist/request/submissionIdentity.js +65 -0
  251. package/dist/request/taskRequest.d.ts +33 -4
  252. package/dist/request/taskRequest.js +341 -156
  253. package/dist/resultFrameKeys.d.ts +1 -0
  254. package/dist/resultFrameKeys.js +4 -1
  255. package/dist/retryStatus.d.ts +8 -0
  256. package/dist/retryStatus.js +57 -9
  257. package/dist/rewindArchiveCapability.js +2 -2
  258. package/dist/rewindEngineAction.d.ts +2 -0
  259. package/dist/rewindEngineAction.js +23 -1
  260. package/dist/rowKind.d.ts +36 -0
  261. package/dist/rowKind.js +97 -0
  262. package/dist/ruleCompileVerdict.d.ts +18 -0
  263. package/dist/ruleCompileVerdict.js +67 -2
  264. package/dist/ruleReadings.d.ts +58 -0
  265. package/dist/ruleReadings.js +132 -0
  266. package/dist/ruleRefusalSentence.d.ts +4 -0
  267. package/dist/ruleRefusalSentence.js +9 -3
  268. package/dist/runCancelContext.d.ts +0 -1
  269. package/dist/runCancelContext.js +1 -2
  270. package/dist/runTerminal.d.ts +5 -0
  271. package/dist/runTerminal.js +52 -17
  272. package/dist/sandboxWire.d.ts +1 -1
  273. package/dist/sandboxWire.js +4 -6
  274. package/dist/scenarioRefusal.js +1 -1
  275. package/dist/scenarioSkills.d.ts +20 -0
  276. package/dist/scenarioSkills.js +72 -0
  277. package/dist/scenarioWire.d.ts +5 -2
  278. package/dist/scenarioWire.js +9 -9
  279. package/dist/sealedKeyCapability.js +2 -2
  280. package/dist/seam.d.ts +2 -0
  281. package/dist/seam.js +3 -3
  282. package/dist/seatContract.d.ts +68 -7
  283. package/dist/seatContract.js +84 -25
  284. package/dist/selfOrchestrationDenial.js +1 -1
  285. package/dist/sessionBackground.d.ts +165 -0
  286. package/dist/sessionBackground.js +781 -0
  287. package/dist/sessionBackgroundHandoff.d.ts +54 -0
  288. package/dist/sessionBackgroundHandoff.js +321 -0
  289. package/dist/sessionMap.js +4 -1
  290. package/dist/sessionPolicyCapability.js +2 -2
  291. package/dist/sessionRulesChildrenReadLive.d.ts +6 -0
  292. package/dist/sessionRulesChildrenReadLive.js +5 -0
  293. package/dist/sessionRulesNamespaceFormsCapability.d.ts +12 -0
  294. package/dist/sessionRulesNamespaceFormsCapability.js +29 -0
  295. package/dist/sessionSyncFileHistoryCapability.js +2 -2
  296. package/dist/settingsCredentialKeyPaths.js +55 -15
  297. package/dist/settingsHooksSalvage.d.ts +1 -0
  298. package/dist/settingsHooksSalvage.js +7 -1
  299. package/dist/settingsLayerAdmission.d.ts +4 -0
  300. package/dist/settingsLayerAdmission.js +16 -0
  301. package/dist/skillToolListsCapability.js +6 -5
  302. package/dist/skillsWireCaps.d.ts +14 -0
  303. package/dist/skillsWireCaps.js +47 -16
  304. package/dist/slashCommandGate.d.ts +24 -0
  305. package/dist/slashCommandGate.js +74 -0
  306. package/dist/slashCommandTable.d.ts +14 -0
  307. package/dist/slashCommandTable.js +99 -0
  308. package/dist/sqlEngineCapability.js +2 -2
  309. package/dist/steerPriorityCapability.js +2 -2
  310. package/dist/storePostureCapability.js +2 -2
  311. package/dist/subagent/agentStateDetail.js +1 -1
  312. package/dist/subagent/engineDelegatedPrompt.d.ts +9 -0
  313. package/dist/subagent/engineDelegatedPrompt.js +38 -7
  314. package/dist/subagent/engineRowStopGate.js +2 -2
  315. package/dist/subagent/engineSubagentResume.js +3 -3
  316. package/dist/subagent/engineTaskHandleWire.d.ts +1 -0
  317. package/dist/subagent/engineTaskHandleWire.js +10 -0
  318. package/dist/submitFailure.d.ts +8 -0
  319. package/dist/submitFailure.js +17 -0
  320. package/dist/supportFloor.d.ts +1 -0
  321. package/dist/supportFloor.js +7 -0
  322. package/dist/terminationIntent.d.ts +55 -0
  323. package/dist/terminationIntent.js +352 -0
  324. package/dist/thinkingLatch.d.ts +69 -0
  325. package/dist/thinkingLatch.js +255 -0
  326. package/dist/thinkingLatchWords.d.ts +7 -0
  327. package/dist/thinkingLatchWords.js +4 -0
  328. package/dist/toolHistoryMismatch.d.ts +8 -0
  329. package/dist/toolHistoryMismatch.js +37 -6
  330. package/dist/toolResult.d.ts +13 -3
  331. package/dist/toolResult.js +159 -32
  332. package/dist/toolResultContent.d.ts +13 -0
  333. package/dist/toolResultContent.js +82 -7
  334. package/dist/toolRoster.d.ts +18 -0
  335. package/dist/toolRoster.js +64 -0
  336. package/dist/turnFailurePresentation.js +27 -90
  337. package/dist/usageLimitAutoResume.d.ts +74 -0
  338. package/dist/usageLimitAutoResume.js +227 -0
  339. package/dist/userAttachmentEnvelope.js +1 -1
  340. package/dist/userAttachmentSeat.js +5 -5
  341. package/dist/userInvokedSkillsCapability.js +2 -2
  342. package/dist/webSearchBackendCapability.js +2 -2
  343. package/dist/wireErrorTriage.js +99 -50
  344. package/dist/wireFailureShape.d.ts +25 -1
  345. package/dist/wireFailureShape.js +92 -5
  346. package/dist/wireRefusalCopy.d.ts +24 -1
  347. package/dist/wireRefusalCopy.js +253 -32
  348. package/dist/wiringFleetDoctor.d.ts +3 -0
  349. package/dist/wiringFleetDoctor.js +21 -0
  350. package/dist/workflow.d.ts +2 -1
  351. package/dist/workflow.js +38 -1
  352. package/dist/workflowClient.d.ts +2 -7
  353. package/dist/workflowsJournalCapability.js +2 -2
  354. package/dist/writeProtectionCapability.js +2 -2
  355. package/docs/INTEGRATION-CLIENTS.md +907 -182
  356. package/package.json +14 -3
  357. package/dist/argvFlagValue.d.ts +0 -8
  358. package/dist/argvFlagValue.js +0 -37
@@ -1,10 +1,17 @@
1
+ import { appendOwnElement, readOwnDataValue } from '../ownKey.js';
1
2
  import { refuseBadDisableSkills, refuseDisableSkillsConflict } from '../disableSkillsWire.js';
2
3
  import { ATTACHMENTS_SPEC_KEYS, AttachmentsSpecRefused } from '../attachmentsWireCaps.js';
3
- import { retiredAttachmentsKeyOf } from '../taskRequestWords.js';
4
+ import { REASONING_EFFORT_WORDS, retiredAttachmentsKeyOf } from '../taskRequestWords.js';
5
+ import { THINKING_LATCH_SET_STATES } from '../thinkingLatchWords.js';
4
6
  import { msg, renderMessage } from '../i18n/translate.js';
7
+ import { PROJECT_MEMORY_DISABLED } from '../projectMemory.js';
8
+ import { frameFamiliesJudgementOf } from '../approvalFrameFamilies.js';
5
9
  import { outputStyleSeatAdmitted } from '../outputStyleSeatCapability.js';
6
10
  import { refuseBadApprovalReach } from '../approvalReachWire.js';
7
- import { HOST_SUPPLIED_KEYS, HostSettingsRefused, UNWIRED_HOST_TURN_SETTINGS, buildHostTurnSettings, hostIntakeSnapshotOf, hostProjectionNotice, hostTurnSettingsOf, intersectHostHooks, rethrowHostResolvedDeferred, } from '../hostTurnSettings.js';
11
+ import { HOST_SUPPLIED_KEYS, UNWIRED_HOST_TURN_SETTINGS, buildHostTurnSettings, hostIntakeSnapshotOf, hostFileHistoryRestoreNotice, hostProjectionNotice, hostTurnSettingsOf, intersectHostHooks, rethrowHostResolvedDeferred, hostSettingsRefusedError, } from '../hostTurnSettings.js';
12
+ import { SUBMISSION_ORIGINS, SUBMISSION_SURFACES, SUBMISSION_TOOLS, submissionAxesOf, } from './submissionIdentity.js';
13
+ import { withAssemblyRefusal } from '../wireFailureShape.js';
14
+ import { isPlainRecord, materializeJsonFaithful, shapeTag } from '../jsonFaithful.js';
8
15
  export const REQUEST_FIELD_MATRIX = [
9
16
  { field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
10
17
  { field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
@@ -18,19 +25,21 @@ export const REQUEST_FIELD_MATRIX = [
18
25
  { field: 'enableFork', lanes: ['interactive', 'print'], live: true, why: '显式布尔才 stamp([503]② fail-OPEN 教训:off 必须显式)', whyUtilityLane: '无座:分叉是会话面的动作,而 side-channel 提交是一次性的独立会话' },
19
26
  { field: 'attachments', lanes: ['interactive', 'print'], live: true, why: 'design/133 + G1 缺省开的 turn 边界 attachment', whyUtilityLane: '无座:turn 边界的附件配置属用户会话;side-channel 提交不带附件' },
20
27
  { field: 'skills', lanes: ['interactive', 'print'], live: true, why: '本地技能目录投影(布局取 CC 同款);两条车道都由 launchRepl 载入面注入', whyUtilityLane: '无座:技能挂的是工具,与零工具面矛盾' },
21
- { field: 'userInvokedSkills', lanes: ['interactive', 'print'], live: true, why: '人用名字点过的技能(sdk `TaskRequest.userInvokedSkills`:非空串数组;这些技能的 allowedTools / disallowedTools 开跑即生效、不出技能卡,只管这一次 run)。两条用户车道都有「人敲 /技能名」的入口 ⇒ 都有座。🔴 放宽方向:值只经片段口给(能力读数 present 才给键);空数组 = 没点名 ⇒ 不 stamp;坏形(不是串数组)装配期响亮拒;名字对不对归引擎判(`config.user_invoked_skill_unknown`)', whyUtilityLane: '无座:技能挂的是工具,与零工具面矛盾(与 skills 一行同理)' },
28
+ { field: 'userInvokedSkills', lanes: ['interactive', 'print'], live: true, why: '人用名字点过的技能(sdk `TaskRequest.userInvokedSkills`:非空串数组;这些技能的 allowedTools / disallowedTools 开跑即生效、不出技能卡,只管这一次 run)。两条用户车道都有「人敲 /技能名」的入口 ⇒ 都有座。🔴 放宽方向:值只经片段口给(能力读数 present 才给键);空数组 = 没点名 ⇒ 不 stamp;坏形(不是串数组)装配期响亮拒;名字对不对归引擎判(`config.user_invoked_skill_unknown`)', whyUtilityLane: '无座:技能挂的是工具,与零工具面矛盾(与 skills 一行同理)', origins: ['user'], whyOriginLimited: '只给用户所写的提交:点名的技能是用户本人敲名字点的(放宽方向:点名即生效、不出技能卡);注入件与客户端自发的旁路提交不许借用户的点名放宽这一次的工具面' },
22
29
  { field: 'mcpServers', lanes: ['interactive', 'print'], live: true, why: '本地 .mcp.json 投影;service 单用户门决定是否兑现', whyUtilityLane: '无座:MCP 服务挂的是工具,与零工具面矛盾' },
23
30
  { field: 'settings.webSearch', lanes: ['interactive', 'print'], live: true, why: 'SEMA_WEBSEARCH_* / settings.json;per-request 配置赢过部署 env', whyUtilityLane: '无座:联网检索是一只工具,与零工具面矛盾' },
24
31
  { field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire', whyUtilityLane: '无座:hook 挂在工具门上(零工具面没有门),而且 side-channel 是壳自己的提交,不该继承用户那份 hook 表' },
32
+ { field: 'settings.approvalFrameFamilies', lanes: ['interactive', 'print'], live: true, why: '这条运行的客户端渲得了哪些审批开卡帧族的**逐运行声明**(服务端提交体 `settings.approvalFrameFamilies`:词数组,值族闭集 tool_approval / approval_request;一条运行只在声明含服务端呈的那一族时才开审批卡,不含 ⇒ 问当场交无人值守政策口、不出卡)。两条用户车道都有审批腿(交互面出卡答人;`-p` 面交宿主的审批口),都得声明;🔴 只在能力读数 confirmed(受理位 `taskSettings.approvalFrameFamilies` 恒真 ∧ 值族位合格)时由装配口自己铸,老服务端(受理位缺席)对表外 settings 子键整条 400', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),没有门控调用、不会问审批 ⇒ 声明没有对象;带上它只会在不认识这一键的老服务端上给这条提交多一种 400' },
25
33
  { field: 'settings.<resolved>', lanes: ['interactive', 'print'], live: true, why: 'settings resolver 的 effective 快照(hooks/env/model + 已解析权限投影)。两条车道同源:交互面 replEntry / print 面 printModeEngine 各跑一遍同一个 resolver,产物经同一个 buildWireSettingsStamp 投影。开放集(子键不可枚举)⇒ unregisteredRequestKeys 放行本车道 settings 的**表外**子键,但表内异车道子键仍点名', whyUtilityLane: '无座:这一位是**开放集 spread**(子键原样即 wire 键),摊开的是用户 settings 的已解析面(权限 / env / 模型)。side-channel 是壳自己的提交:摊上去既无用(零工具面没有工具门求值权限)又把用户的配置面带进了一条他没参与的提交。🔴 无座 ⇒ 本车道整只快照不产。🔴 **本行无座的前提由构造期校验兜住**:剥掉的是用户显式的 deny / ask,所以 `refuseUtilityWithoutToolUnload` 强制这条车道必须带字面 `excludeAllTools: true` —— 否则产出会是一条「既没卸工具、又没带用户 deny」的请求(比无人值守车道同一份输入更宽)' },
26
34
  { field: 'repo', lanes: ['interactive', 'print'], live: true, why: 'scan/code-review 场景的仓库坐标(passthrough;server 是权威校验方,本层不铸第二判官)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
27
35
  { field: 'memoryCapture', lanes: ['interactive', 'print', 'utility'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryWrite:false 不是一个轴(那是「本 run 只读平面」)。⚠️ parked 续跑腿结构性拿不到声明(决策/续跑端点从持久 body 重建 spec),那是引擎侧事实,不是本层的漏', whyUtilityLane: '有座:side-channel 提交是壳自己的一次性问答,**绝不该**进用户的长期记忆 —— 这一位正是那句声明,且它是收紧方向,带上去只会更严' },
28
36
  { field: 'memory', lanes: ['interactive', 'print', 'utility'], live: true, why: '这一次运行不挂记忆面的**请求级**开关(server §12.7 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryCapture(采集面)不是一个轴、可并存;与 memoryWrite:false(只读平面)也不是一个轴', whyUtilityLane: '有座:side-channel 提交不需要挂记忆面(不挂记忆段 / 索引 / 三只记忆工具),省 token 且零记忆写入;与 memoryCapture 不是一个轴,两条都带' },
29
- { field: 'fileHistory', lanes: ['interactive', 'print'], live: true, why: '「这一次运行别捕获文件历史」的**请求级声明**(server 7.104.0 提交体键,单成员闭集 "disabled";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,子代继承)。两条用户车道都是提交面、都跑在用户的文件上 ⇒ 都能声明;「本会话不捕获」= 每个提交都带(续跑腿重放同一个体)。🔴 只在能力读数 `on-optional` 时发(老 server 对表外键 400),值由片段口 `fileHistoryDeclarationField` 唯一铸出。与 rewind 还原键同交 ⇒ 上游 400 `request.field_conflict`', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),不会改任何文件 ⇒ 没有可捕获的东西;带上它只会在不认识这一键的老 server 上给这条提交多一种 400' },
37
+ { field: 'fileHistory', lanes: ['interactive', 'print'], live: true, why: '「这一次运行别捕获文件历史」的**请求级声明**(server 7.104.0 提交体键,单成员闭集 "disabled";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,子代继承)。两条用户车道都是提交面、都跑在用户的文件上 ⇒ 都能声明;「本会话不捕获」= 每个提交都带(续跑腿重放同一个体)。🔴 只在能力读数 `on-optional` 时发(老 server 对表外键 400),值由装配产物 `fileHistory` 行铸出(AS-4)。与 rewind 还原键同交 ⇒ 上游 400 `request.field_conflict`', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),不会改任何文件 ⇒ 没有可捕获的东西;带上它只会在不认识这一键的老 server 上给这条提交多一种 400' },
38
+ { field: 'projectMemory', lanes: ['interactive', 'print', 'utility'], live: true, why: '「这一次运行别读部署的项目上下文(项目指令文件与项目层)」的**请求级声明**(core 7.55.0 `TaskSpec.projectMemory`,单成员闭集 "disabled";子代继承、续跑腿重放同一个体)。三条车道都是提交面、引擎对每一条都会调部署的项目上下文座 ⇒ 都能声明(关闭是收紧方向;不关的车道会把项目指令文件递给模型)。缺席 = 座照常读 = 不带键,本层不铸 "on"。🔴 宿主经见位才发的片段口取值,服务端能力位 `projectMemory` 为真才给键(老服务端对表外顶层键整条 400)', whyUtilityLane: '有座:side-channel 提交虽然零工具面,引擎仍对它调部署的项目上下文座;壳在「不读项目指令文件」的模式(`--safe-mode` / `--bare`)下,这一条旁路提交也不该把项目指令文件带给模型 —— 这一位是收紧方向,带上去只会更严(与 memory / memoryCapture 同理由)' },
30
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' },
31
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 那一行),从不续任何已有会话 ⇒「必须已存在」对它没有意义,带上去只会让每一次提交都被拒' },
32
41
  { field: 'disableSkills', lanes: ['interactive', 'print'], live: true, why: '「这一发一只技能都不装」的**请求级声明**(出厂技能、调用方给的技能、子代定义里的技能一个都不进引擎;其余工具照挂 —— 与 excludeAllTools 是两件事,两键同带各自生效)。两条用户车道都是提交面 ⇒ 都能声明。本层只认字面 true(false 与缺席同义、不发);非布尔坏值响亮拒(上游对它 400);与非空 skills 或任一非空 agents[].skills 同在 ⇒ 装配期按名响亮拒(上游对这一组合 400),兜底层落 agents 之后同判。🔴 只在能力位读数为真时发(老服务端对表外键整条 400),值由片段口 disableSkillsDeclaration 唯一铸出', whyUtilityLane: '无座:同 skills 一行 —— side-channel 是零工具面,装不装技能都无从谈起' },
33
- { 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 要的是一次小提交的即时答案;「无人值守才需要引擎自证」这条理由在本车道上让位给「小预算优先」' },
34
43
  { field: 'limits', lanes: ['print', 'utility'], live: true, why: 'P2-3-b:`-p` 的预算护栏(--max-* flag 族),交互 REPL 由人随时 Esc', whyUtilityLane: '有座:side-channel 必须带预算护栏(墙钟 / 输出 token / 轮数)—— 它同样没有人看着,而且没有后续回合可以中断' },
35
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`;这一位是它旁边那条「连问都别问」的声明,不是它的降级替身)' },
36
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' },
@@ -38,7 +47,7 @@ export const REQUEST_FIELD_MATRIX = [
38
47
  { field: 'outputRetries', lanes: ['print', 'utility'], live: true, why: '结构化输出的重试轮数(sdk `TaskRequest.outputRetries`;与 `outputSchema` 是**成对旋钮** —— 没有 schema 时引擎忽略它)。🔴 座位集与 `outputSchema` 那一行**逐字相同**:一半有座一半没座 = 端调得动重试轮数却调不动它所服务的约束(同座孪生分开定座,正是「主轴改了、孪生漏了」那一族)。上游接受域很窄(有限、≥1、向下取整、封顶 10),**表外一律整键省略**并回落引擎缺省 ⇒ 本层对坏值**响亮拒**(见 `refuseBadOutputRetries`),但**不预夹** [1,10]:夹是上游的事,本层不铸第二判官', whyUtilityLane: '有座:与 outputSchema 同座孪生 —— side-channel 要机读小答案时,重试轮数是同一个旋钮的另一半' },
39
48
  { field: 'maxCostUsd', lanes: ['print', 'utility'], live: true, why: '花费上限声明(sdk `TaskRequest.maxCostUsd`,请求体**顶层**位;上游向下夹到部署上限 —— 只许要得更少)。与 `limits` 同一条理由只在无人值守车道:交互面由人随时中断。🔴 坏值(非数 / 非有限 / 非正)在本层**响亮拒**:上游对这一位的坏值是安静忽略并回落部署上限,不是 4xx(见 `refuseBadMaxCostUsd`)', whyUtilityLane: '有座:小预算是本车道的定义之一(与 limits 同一条理由);上游向下夹到部署上限,只许要得更少' },
40
49
  { field: 'maxTokens', lanes: ['print', 'utility'], live: true, why: '整任务 token 预算上限声明(sdk `TaskRequest.maxTokens`,请求体**顶层**位,与 `maxCostUsd` 同属上游那一族「调用方要求的上限」:向下夹到部署上限 —— 只许要得更少)。🔴 座位集与 `maxCostUsd` 那一行**逐字相同**:同一条预算轴的两种计价单位,分开定座就是让其中一条静默漂。🔴 坏值(非数 / 非有限 / 非正 / 非整)在本层**响亮拒**:上游对这一位的坏值是安静按「调用方没给」处理并回落部署上限,不是 4xx —— 部署没设上限时这一次运行就没有 token 闸,而声明人不会知道(见 `refuseBadMaxTokens`)', whyUtilityLane: '有座:与 maxCostUsd 同轴孪生 —— 小预算是本车道的定义之一,两种计价单位都该能声明' },
41
- { field: 'attachmentIds', lanes: ['interactive'], live: true, why: '附件**字节通道**(uploadAttachment 回执 id;server ≥1.289 绑定会话并把文件物化进 run 的 attachments/)。有上传入口的端才有 id 可引用:web/desktop 有,壳走 images / 本地文件路径,`-p` 车道连 uploadAttachment 都没有 ⇒ print 缺席是**没有来源**,不是漏。🔴 与上面的 `attachments` 同名不同物(那是 turn 边界的配置键),两条永远不许合并', whyUtilityLane: '无座:预上传附件句柄要先有上传入口才有 id 可引用,而 side-channel 没有上传面' },
50
+ { field: 'attachmentIds', lanes: ['interactive'], live: true, why: '附件**字节通道**(uploadAttachment 回执 id;server ≥1.289 绑定会话并把文件物化进 run 的 attachments/)。有上传入口的端才有 id 可引用:web/desktop 有,壳走 images / 本地文件路径,`-p` 车道连 uploadAttachment 都没有 ⇒ print 缺席是**没有来源**,不是漏。🔴 与上面的 `attachments` 同名不同物(那是 turn 边界的配置键),两条永远不许合并', whyUtilityLane: '无座:预上传附件句柄要先有上传入口才有 id 可引用,而 side-channel 没有上传面', origins: ['user'], whyOriginLimited: '只给用户所写的提交:上传件句柄是用户本人上传、挂在这一条消息上的文件;注入件与客户端自发的旁路提交不带用户的上传件(带上去 = 把用户的文件交给一条不是他写的提交)' },
42
51
  { field: 'excludeTools', lanes: ['interactive', 'print', 'utility'], live: true, why: 'design/161 W3(2026-07-30):真 wire 键(SDK types `excludeTools`)且早在 seatContract `START_SESSION_OPTION_KEYS` 里,却是 desktop 手写 taskReq 的第 9 键、矩阵外——而 stamp 门对未登记键**静默丢弃**,真实危险形=「用户显式排除的工具被静默放回」(权限方向回归,类型层不报)。今天只有 desktop 在发;壳的工具面走 roster/interactiveTools 另一条路。0.77.0 补 print 座(三问:① wire 位与车道无关 —— sdk 具名位、提交体真收 `body.excludeTools`;② 来源 —— print 今天没有排除入口,但这是**收紧方向**的声明,传了只进回执 = 端不读回执就把用户排除的工具放回;③ 口径 —— 仍是 live 门后的位,与交互面同一行)', whyUtilityLane: '有座:逐名排除是卸载声明旁边的第二条收紧声明(两键同带各自生效)。⚠️ 它**不是** excludeAllTools 的降级替身 —— 本车道强制带卸载声明(见 `refuseUtilityWithoutToolUnload`),不认那一键的上游会把整条提交拒掉,那时正解是换车道或不发' },
43
52
  { field: 'allowTools', lanes: ['interactive', 'print'], live: true, why: '引擎的可用性选择器(收紧方向的声明:只给点名的工具;`[]` = 什么都不给,照发)。两条用户车道都是提交面、都有「只给这几只工具」的用户入口(终端 `--tools`)⇒ 都有座;坏形(不是串数组)在装配期响亮拒,不静默删键', whyUtilityLane: '无座:side-channel 恒带 excludeAllTools(零工具面),可用性选择器在零工具面上没有可选的东西' },
44
53
  { field: 'excludeAllTools', lanes: ['interactive', 'print', 'utility'], live: true, why: '0.78.0(sdk 11.x 具名位 `excludeAllTools`,server ≥7.90.0 请求面):整只工具面卸载 —— 调用方工具 / 脚手架 / 手 / MCP / A2A / skills 一个都不挂,终局必然纯文本(wire 上 `wiring_manifest.tools.count === 0`,整条流零 tool_start / tool_end / tool_disclosure)。与 excludeTools 是两种语义、刻意不折叠(`"*"` 是一个合法的工具名,「卸全部」不能写成 `excludeTools:["*"]`);两键同带各自生效。类型只收字面 `true`(server 只把 literal true 写上任务规格,`false` = 替引擎重申它自己的缺省 ⇒ 不发、与缺席同义、不进回执);坏值响亮拒(收紧方向的声明不许被打字错误安静丢掉)。每条车道都是提交面 ⇒ 都能声明。探测靠键闭集、没有能力位:没接线的 server(≤7.89.x)400 `request.body_shape` 并在 `unknownKeys` 里逐字点名,本层不替它猜;A2A 入站面不承载本键(零工具面走 skill 路由)', whyUtilityLane: '有座,而且**本车道强制**:零工具面就是这条车道的定义 —— side-channel 提交整只工具面卸载,终局必然纯文本。🔴 缺这一位(或给 `false`)⇒ 构造期响亮拒(`refuseUtilityWithoutToolUnload`):本车道上一整片「无座」的理由都压在这个前提上,其中 `settings.<resolved>` 那一行剥掉的是用户显式的 deny / ask ——前提不成立时产出会是一条「既没卸工具、又没带用户 deny」的请求' },
@@ -46,8 +55,8 @@ export const REQUEST_FIELD_MATRIX = [
46
55
  { field: 'systemPrompt', lanes: ['interactive', 'print', 'utility'], live: false, why: '整段替换系统提示(sdk `TaskRequest.systemPrompt`;上游字符数超限 400 响亮拒,本层原样透传)。三条车道都有来源:交互面 = CC QueryParams.systemPrompt,无人值守面 = `--system-prompt[-file]` 一类旗,side-channel 面 = 壳整段给出的那一份。与追加位 `appendSystemPrompt` 不是一个位(那一位归 live 兜底层,见 `LIVE_DEFAULT_FIELDS`)', whyUtilityLane: '有座:side-channel 提交的系统提示由壳整段给出 —— 它不是用户会话的续谈,不该继承用户那一份' },
47
56
  { field: 'reasoningEffort', lanes: ['interactive', 'print', 'utility'], live: false, why: '思考档位(sdk `TaskRequest.reasoningEffort` 七词闭集;上游对表外词 400 响亮拒 ⇒ 本层不预铸第二判官)。档位是请求级的位、与车道无关:交互面 = `/effort` 拨盘,无人值守面 = 档位旗。🔴 端交的必须是**档位词**:「思考 token 数」今天没有上游对位(见 `UPSTREAM_ABSENT_REQUEST_INTENTS` 的 `maxThinkingTokens`),别把一个数字折成档位词发出去', whyUtilityLane: '有座:side-channel 该用显式低档(省钱省时);与 model 同一条理由 —— 本车道上这两位都由壳显式给,不跟用户会话的拨盘走' },
48
57
  { field: 'model', lanes: ['interactive', 'print', 'utility'], live: false, why: '模型选择(sdk `TaskRequest.model`;交互面 = `/model` 中途换模型的拨盘,无人值守面 = 模型旗 / 部署缺省那一路,side-channel 面 = 壳显式挑的那一只)。三条车道都有来源、都是提交面。🔴 0.79.0 改口:修前「print 的模型走另一条路」这句理由被用户面实测证伪 —— 无人值守车道传了模型名却整键不上请求体,于是同一个坏名字在交互态被引擎 400 点名、在无人值守态却安静换成部署缺省并在首帧回显一个没生效的名字。一个**选择**类声明被静默丢掉,后果与收紧类声明被静默丢掉同形(端不读回执就以为生效)—— 与 `excludeTools` 补 print 座同律。本层原样透传、不预判模型名(名字的合法性归引擎,本层不铸第二判官)', whyUtilityLane: '有座:side-channel 显式挑便宜的那只模型(不跟用户会话的 `/model` 拨盘走)' },
49
- { field: 'images', lanes: ['interactive', 'print'], live: false, why: '图片输入(sdk `TaskRequest.images`;base64 / url 两形,原样透传)。两条用户车道都是提交面、都有来源:交互面 = 粘贴图与 `@` 图片像素,一次性车道 = 结构化输入里的图片块。张数、单张与合计的座位判定归人类提交装配口 `encodeHumanTurn`(两条车道同一份判定,本表不另判);请求体与图片张数的上限归上游判', whyUtilityLane: '无座:贴图是交互动作,side-channel 的提交体里没有图片块' },
50
- { field: 'userAttachments', lanes: ['interactive', 'print'], live: false, why: '请求层附件(sdk `TaskRequest.userAttachments`:挂在这次提交目标上的 `@` 文本类附件条目,引擎铸帧、在第一次模型调用之前判形)。两条用户车道都是提交面、都经同一只装配口 `encodeHumanTurn`(条目只在这台引擎的席位读数判 `yes` 时出;老服务端对这一键 400,所以没有条目就不发);空数组 = 没带附件 ⇒ 整键不 stamp。座位集与 `images` 一行逐字相同', whyUtilityLane: '无座:side-channel 是壳自己的一次性提交,没有用户 `@` 附件' },
58
+ { field: 'images', lanes: ['interactive', 'print'], live: false, why: '图片输入(sdk `TaskRequest.images`;base64 / url 两形,原样透传)。两条用户车道都是提交面、都有来源:交互面 = 粘贴图与 `@` 图片像素,一次性车道 = 结构化输入里的图片块。张数、单张与合计的座位判定归人类提交装配口 `encodeHumanTurn`(两条车道同一份判定,本表不另判);请求体与图片张数的上限归上游判', whyUtilityLane: '无座:贴图是交互动作,side-channel 的提交体里没有图片块', origins: ['user'], whyOriginLimited: '只给用户所写的提交:图片是用户本人贴进 / 点名进这一条消息的内容;注入件(计划复核结局等)与客户端自发的旁路提交没有用户贴的图片,带上去就是把别处的图片当成这一发的输入' },
59
+ { field: 'userAttachments', lanes: ['interactive', 'print'], live: false, why: '请求层附件(sdk `TaskRequest.userAttachments`:挂在这次提交目标上的 `@` 文本类附件条目,引擎铸帧、在第一次模型调用之前判形)。两条用户车道都是提交面、都经同一只装配口 `encodeHumanTurn`(条目只在这台引擎的席位读数判 `yes` 时出;老服务端对这一键 400,所以没有条目就不发);空数组 = 没带附件 ⇒ 整键不 stamp。座位集与 `images` 一行逐字相同', whyUtilityLane: '无座:side-channel 是壳自己的一次性提交,没有用户 `@` 附件', origins: ['user'], whyOriginLimited: '只给用户所写的提交:请求层附件是用户本人 `@` 进这一条消息的附件;注入件与客户端自发的旁路提交不带用户的附件' },
51
60
  { field: 'resumeAt/resumeAtMode/restoreFiles/rewindFilesTo', lanes: ['interactive'], live: false, why: 'E18 `/rewind` 是交互命令;四键同生同灭(CC-145:core 6.0.0 起 both 臂发 restoreFiles;CC-223 起退役键 rewindFiles 不再有座 —— 交它按表外子键响亮拒;resumeAtMode 是 both/conversation 两臂恒带的排他截语义位)', whyUtilityLane: '无座:回退是交互命令,而 side-channel 是一次性的独立会话,没有可回退的历史' },
52
61
  { field: 'clientContext', lanes: ['interactive'], live: false, why: 'IANA 时区 + 可选邮箱 → core 本地化 `# Environment` 的 today;print 同样跑在用户机器上,缺席让引擎误标 (UTC)', gap: true, whyUtilityLane: '无座:本地化 today 的用途在用户会话的环境段,而 side-channel 的系统提示由壳整段给出 —— 这一列我判不准要不要(与本行 `gap` 同一条待办:要补先单独立项)' },
53
62
  { field: 'scratchpadDir', lanes: ['interactive'], live: true, why: '[816]③ per-session 暂存目录;print 也有 session,缺席让 `-p` 的工具写不进 exemptDir', gap: true, whyUtilityLane: '无座:per-session 暂存目录是给**工具**写的,零工具面上没人写' },
@@ -70,12 +79,14 @@ export const TASK_REQUEST_OMISSION_CAUSES = Object.freeze([
70
79
  'upstream_absent',
71
80
  'other_channel',
72
81
  'off_lane',
82
+ 'off_origin',
73
83
  'not_live',
74
84
  ]);
75
85
  const TASK_REQUEST_OMISSION_CAUSE_SENTENCES = Object.freeze({
76
86
  upstream_absent: 'this build knows the intent but the engine has no request field for it, so nothing was sent and nothing will be until that field exists upstream — retire the knob, or wait for the upstream field',
77
87
  other_channel: 'this is a real request field, but the request assembler never sets it — it belongs to the live-defaults layer, so hand the value to that layer instead of to the assembler',
78
88
  off_lane: 'this request lane has no seat for the key, so the value was not sent — the lane table records why that lane does not carry it; submit on a lane that seats it, or stop passing it here',
89
+ off_origin: 'the key has a seat on this lane, but not for the origin of this submission, so the value was not sent — the lane table records which origins carry it and why; pass it only on a submission whose origin is listed there, or stop passing it here',
79
90
  not_live: 'the key has a seat on this lane but only reaches the wire on a live submission, and this submission was not live — nothing is wrong here: a mock or fixture submission keeps its request shape fixed',
80
91
  });
81
92
  export function taskRequestOmissionCauseDetail(cause) {
@@ -98,6 +109,10 @@ const TASK_REQUEST_OMISSION_CAUSE_NOTICE_PARTS = Object.freeze({
98
109
  lead: 'Not sent with this request',
99
110
  reason: 'this kind of request does not carry this setting, so giving it or leaving it out makes no difference to what this request sends',
100
111
  },
112
+ off_origin: {
113
+ lead: 'Not sent with this request',
114
+ reason: 'the request was marked as not written by a person (for example an automatic follow-up), and this setting is only sent with requests a person writes themselves',
115
+ },
101
116
  not_live: {
102
117
  lead: 'Not sent with this request',
103
118
  reason: 'the request was marked as not going to a live engine (for example an offline or test run), and this setting is only sent on requests that go to one',
@@ -169,12 +184,16 @@ const INPUT_KEY_ROUTES = {
169
184
  },
170
185
  memoryCapture: row('memoryCapture'),
171
186
  inheritEnv: row('inheritEnv'),
187
+ projectMemory: row('projectMemory'),
172
188
  requireExistingSession: row('requireExistingSession'),
173
189
  disableSkills: row('disableSkills'),
174
190
  rewind: row('resumeAt/resumeAtMode/restoreFiles/rewindFilesTo'),
175
191
  repo: row('repo'),
176
192
  allowTools: row('allowTools'),
177
193
  hostSettings: { via: 'control' },
194
+ approvalFrameFamiliesReading: { via: 'control' },
195
+ thinkingLatch: { via: 'control' },
196
+ reasoningFloor: { via: 'control' },
178
197
  settings: { via: 'settings' },
179
198
  };
180
199
  const HOST_TOP_KEY_ROUTES = {
@@ -184,15 +203,21 @@ const HOST_TOP_KEY_ROUTES = {
184
203
  const HOST_TOP_KEYS = Object.freeze(Object.keys(HOST_TOP_KEY_ROUTES));
185
204
  const _hostTopKeysPin = [true];
186
205
  void _hostTopKeysPin;
206
+ const _projectMemoryWordPin = [true, true];
207
+ void _projectMemoryWordPin;
208
+ const _thinkingLatchWordPin = [true, true];
209
+ void _thinkingLatchWordPin;
187
210
  const HOST_TOP_ASK_AFTER = 'memoryCapture';
188
211
  const HOST_SETTINGS_SUBKEYS = Object.freeze(HOST_SUPPLIED_KEYS.filter((k) => k.startsWith('settings.')).map((k) => k.slice('settings.'.length)));
189
- const _hostSubkeysPin = [true, true, true];
212
+ const MINTED_SETTINGS_SUBKEYS = Object.freeze(['approvalFrameFamilies']);
213
+ const _hostSubkeysPin = [true, true, true, true];
190
214
  void _hostSubkeysPin;
191
215
  const SETTINGS_KEY_ROUTES = {
192
216
  resolved: row('settings.<resolved>'),
193
217
  webSearch: row('settings.webSearch'),
194
218
  ultracode: row('settings.ultracode'),
195
219
  hooks: row('settings.hooks'),
220
+ approvalFrameFamilies: row('settings.approvalFrameFamilies'),
196
221
  outputStyle: { via: 'always' },
197
222
  };
198
223
  export const UPSTREAM_ABSENT_REQUEST_INTENTS = Object.freeze(Object.entries(INPUT_KEY_ROUTES).flatMap(([key, route]) => route.via === 'fixed' && route.cause === 'upstream_absent' ? [Object.freeze({ key, why: route.why })] : []));
@@ -226,10 +251,11 @@ const OMISSION_JUDGES = {
226
251
  upstream_absent: s => s.via === 'fixed' && s.cause === 'upstream_absent',
227
252
  other_channel: s => s.via === 'fixed' && s.cause === 'other_channel',
228
253
  off_lane: (s, lane) => s.via === 'matrix' && !s.spec.lanes.includes(lane),
254
+ off_origin: (s, _lane, _live, origin) => s.via === 'matrix' && origin !== undefined && s.spec.origins !== undefined && !s.spec.origins.includes(origin),
229
255
  not_live: (s, _lane, live) => s.via === 'matrix' && s.spec.live && !live,
230
256
  };
231
- const omissionCauseOf = (s, lane, live) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live));
232
- const hostRefusalNoticeOf = (intake, lane, live) => {
257
+ const omissionCauseOf = (s, lane, live, origin) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live, origin));
258
+ const hostRefusalNoticeOf = (intake, lane, live, origin) => {
233
259
  const verdictNotice = intake.notice ?? hostProjectionNotice();
234
260
  try {
235
261
  const refusing = intake.refusingKeys;
@@ -238,7 +264,7 @@ const hostRefusalNoticeOf = (intake, lane, live) => {
238
264
  const turnNotices = intake.turnNotices;
239
265
  const aligned = turnNotices !== undefined && turnNotices.length === refusing.length;
240
266
  for (const [i, key] of refusing.entries()) {
241
- if (!hostRefuseRowSeated(key, lane, live))
267
+ if (!hostRefuseRowSeated(key, lane, live, origin))
242
268
  continue;
243
269
  return aligned ? (turnNotices[i] ?? verdictNotice) : verdictNotice;
244
270
  }
@@ -248,10 +274,10 @@ const hostRefusalNoticeOf = (intake, lane, live) => {
248
274
  return verdictNotice;
249
275
  }
250
276
  };
251
- const hostRefuseRowSeated = (key, lane, live) => {
277
+ const hostRefuseRowSeated = (key, lane, live, origin) => {
252
278
  if (hasOwn(HOST_TOP_KEY_ROUTES, key)) {
253
279
  const top = HOST_TOP_KEY_ROUTES[key];
254
- return top === undefined || (top.via !== 'matrix' && top.via !== 'fixed') || omissionCauseOf(standingOf(top), lane, live) === undefined;
280
+ return top === undefined || (top.via !== 'matrix' && top.via !== 'fixed') || omissionCauseOf(standingOf(top), lane, live, origin) === undefined;
255
281
  }
256
282
  if (!key.startsWith('settings.'))
257
283
  return true;
@@ -262,7 +288,7 @@ const hostRefuseRowSeated = (key, lane, live) => {
262
288
  if (route === undefined || route.via === 'always')
263
289
  return true;
264
290
  if (route.via === 'matrix' || route.via === 'fixed')
265
- return omissionCauseOf(standingOf(route), lane, live) === undefined;
291
+ return omissionCauseOf(standingOf(route), lane, live, origin) === undefined;
266
292
  return true;
267
293
  };
268
294
  const REQUEST_LANE_SET = { interactive: true, print: true, utility: true };
@@ -273,6 +299,60 @@ const refuseBadLane = (lane) => {
273
299
  throw new TypeError(`buildTaskRequest: 车道实参只接受 ${Object.keys(REQUEST_LANE_SET).map(l => `'${l}'`).join(' / ')} —— 收到 ${coarseShape(lane)}。` +
274
300
  '表外的车道词会让车道表里每一行都判「不属于本车道」,请求体只剩 objective 与 sessionId 而没有任何信号,所以在装配前拒。错误文本只报形状,不回显值。');
275
301
  };
302
+ const laneAxesOf = (arg, port) => {
303
+ if (typeof arg !== 'object' || arg === null) {
304
+ refuseBadLane(arg);
305
+ return { lane: arg, origin: undefined };
306
+ }
307
+ const axes = submissionAxesOf(arg);
308
+ if (axes !== undefined)
309
+ return axes;
310
+ let shape;
311
+ try {
312
+ shape = coarseShape(arg);
313
+ }
314
+ catch {
315
+ shape = 'typeof=object / unreadable';
316
+ }
317
+ const err = new TypeError(renderMessage(msg('config.taskRequest.submissionIdentityMalformed', {
318
+ shape,
319
+ lanes: REQUEST_LANES.join(' / '),
320
+ surfaces: SUBMISSION_SURFACES.join(' / '),
321
+ origins: SUBMISSION_ORIGINS.join(' / '),
322
+ tools: SUBMISSION_TOOLS.join(' / '),
323
+ })));
324
+ throw port === 'assembly' ? withAssemblyRefusal(err, 'input_malformed') : err;
325
+ };
326
+ const originAxisFaultOf = (origins, why, lanes) => {
327
+ if (origins === undefined)
328
+ return '写了 whyOriginLimited 却没写 origins(理由没有对象)';
329
+ if (typeof why !== 'string' || why.length === 0)
330
+ return '写了 origins 却没写 whyOriginLimited(来源受限必须带理由)';
331
+ if (!Array.isArray(origins) || origins.length === 0)
332
+ return 'origins 不是非空数组';
333
+ if (!origins.every((o) => SUBMISSION_ORIGINS.includes(o)) || new Set(origins).size !== origins.length) {
334
+ return 'origins 里有出身闭集外的词或重复词';
335
+ }
336
+ if (origins.length === SUBMISSION_ORIGINS.length)
337
+ return 'origins 是全集(与缺省同义:两种写法说同一件事,删掉这一位)';
338
+ if (!origins.includes('user'))
339
+ return 'origins 不含 user(成因 off_origin 的终端用户句说「这一发被标成不是人写的」,对这一行会是假话;真有这种行时先加成因词与句子)';
340
+ if (!origins.includes('shell') && lanes.includes('utility'))
341
+ return 'origins 排除壳出身却占 utility 座(身份形派生到 utility 必是壳出身,那个座永远到不了)';
342
+ return undefined;
343
+ };
344
+ const refuseOriginAxisDrift = () => {
345
+ for (const r of REQUEST_FIELD_MATRIX) {
346
+ const origins = r.origins;
347
+ const why = r.whyOriginLimited;
348
+ if (origins === undefined && why === undefined)
349
+ continue;
350
+ const fault = originAxisFaultOf(origins, why, r.lanes);
351
+ if (fault === undefined)
352
+ continue;
353
+ throw new Error(`buildTaskRequest: 车道表 REQUEST_FIELD_MATRIX 的「${r.field}」一行来源轴不自洽 —— ${fault}。这是本包内部缺陷,不是调用方入参的问题。`);
354
+ }
355
+ };
276
356
  const present = (v) => v !== undefined && v !== null;
277
357
  const rewindSubKeys = () => {
278
358
  const r = INPUT_KEY_ROUTES.rewind;
@@ -290,6 +370,7 @@ const VALUE_LEVEL_IN_PLAY = Object.freeze({
290
370
  memoryCapture: (v) => v === 'off',
291
371
  memory: (v) => v === 'off',
292
372
  fileHistory: (v) => v === FILE_HISTORY_DISABLED,
373
+ projectMemory: (v) => v === PROJECT_MEMORY_DISABLED,
293
374
  requireExistingSession: (v) => v === true,
294
375
  disableSkills: (v) => v === true,
295
376
  rewind: (v) => typeof v === 'object' && v !== null && rewindSubKeys().some((k) => hasOwn(v, k) && present(v[k])),
@@ -304,15 +385,16 @@ const snapshotByName = (source, keys, seed = {}) => {
304
385
  const UNKNOWN_KEY_DISPLAY_MAX = 64;
305
386
  const MAX_UNKNOWN_KEYS_SHOWN = 16;
306
387
  const keyForDisplay = (k) => k.length > UNKNOWN_KEY_DISPLAY_MAX ? `${JSON.stringify(k.slice(0, UNKNOWN_KEY_DISPLAY_MAX))}(已截短)` : JSON.stringify(k);
307
- export function taskRequestSeatOf(key, lane) {
308
- refuseBadLane(lane);
388
+ export function taskRequestSeatOf(key, laneOrIdentity) {
389
+ const { lane, origin } = laneAxesOf(laneOrIdentity, 'seat');
390
+ refuseOriginAxisDrift();
309
391
  const route = typeof key === 'string' ? routeOfInputKey(key) : undefined;
310
392
  if (route === undefined)
311
393
  return SEAT_UNKNOWN;
312
394
  if (route.via === 'always' || route.via === 'control' || route.via === 'settings')
313
395
  return SEATED_UNGATED;
314
396
  const standing = standingOf(route);
315
- const cause = omissionCauseOf(standing, lane, true);
397
+ const cause = omissionCauseOf(standing, lane, true, origin);
316
398
  if (cause !== undefined && cause !== 'not_live')
317
399
  return Object.freeze({ seat: 'none', cause });
318
400
  return standing.via === 'matrix' && standing.spec.live ? SEATED_LIVE_GATED : SEATED_UNGATED;
@@ -324,99 +406,6 @@ const foreignSettingsSubKeys = (lane) => REQUEST_FIELD_MATRIX.filter(f => f.fiel
324
406
  const namedSettingsSubKeys = () => REQUEST_FIELD_MATRIX.filter(f => f.field.startsWith('settings.') && f.field !== 'settings.<resolved>').map(f => f.field.slice('settings.'.length));
325
407
  const PERMISSION_FACE_KEY = 'permissions';
326
408
  const coarseShape = (v) => v === null ? 'typeof=object / null' : Array.isArray(v) ? 'typeof=object / array' : `typeof=${typeof v}`;
327
- const shapeTag = (v) => {
328
- try {
329
- const proto = Object.getPrototypeOf(v);
330
- const carrier = proto === null
331
- ? 'null-prototype'
332
- : String(proto?.constructor?.name ?? 'anonymous');
333
- return `typeof=${typeof v} / ${Object.prototype.toString.call(v)} / prototype=${carrier}`;
334
- }
335
- catch {
336
- return `typeof=${typeof v} / shape=unreadable`;
337
- }
338
- };
339
- const isPlainRecord = (v) => {
340
- if (typeof v !== 'object' || v === null)
341
- return false;
342
- const proto = Object.getPrototypeOf(v);
343
- if (proto === null)
344
- return true;
345
- if (typeof proto !== 'object')
346
- return false;
347
- if (!Object.prototype.hasOwnProperty.call(proto, 'constructor'))
348
- return false;
349
- const ctor = proto.constructor;
350
- if (typeof ctor !== 'function')
351
- return false;
352
- if (Function.prototype.toString.call(ctor) !== Function.prototype.toString.call(Object))
353
- return false;
354
- return ctor.prototype === proto;
355
- };
356
- const materializeJsonFaithful = (v, path, seen = new Set()) => {
357
- if (v === null)
358
- return { value: null };
359
- const t = typeof v;
360
- if (t === 'string' || t === 'boolean')
361
- return { value: v };
362
- if (t === 'number') {
363
- return Number.isFinite(v) ? { value: v } : { bad: `${path}(非有限数 ⇒ 序列化成 null)` };
364
- }
365
- if (t !== 'object')
366
- return { bad: `${path}(typeof=${t} 不是 JSON 值)` };
367
- const o = v;
368
- if (seen.has(o))
369
- return { bad: `${path}(循环引用)` };
370
- const toJSONProp = o.toJSON;
371
- if (typeof toJSONProp === 'function')
372
- return { bad: `${path}(自报 toJSON ⇒ 字节被改写)` };
373
- seen.add(o);
374
- try {
375
- if (Array.isArray(o)) {
376
- const rawLen = o.length;
377
- if (typeof rawLen === 'bigint' || typeof rawLen === 'symbol') {
378
- return { bad: `${path}(length 是 ${typeof rawLen} ⇒ 原生序列化直接抛,归 0 会静默发空表)` };
379
- }
380
- let lenNum;
381
- try {
382
- lenNum = +rawLen;
383
- }
384
- catch {
385
- return { bad: `${path}(length 按 ToNumber 转换即抛(BigInt/symbol 形)⇒ 原生序列化也抛,归 0 会静默发空表)` };
386
- }
387
- if (lenNum === Number.POSITIVE_INFINITY) {
388
- return { bad: `${path}(length 是 +Infinity ⇒ 原生序列化抛 RangeError,归 0 会静默发空表)` };
389
- }
390
- const len = Number.isNaN(lenNum)
391
- ? 0
392
- : Math.min(Math.max(0, Math.trunc(lenNum)), Number.MAX_SAFE_INTEGER);
393
- const items = [];
394
- for (let i = 0; i < len; i++) {
395
- const r = materializeJsonFaithful(o[i], `${path}[${i}]`, seen);
396
- if ('bad' in r)
397
- return r;
398
- items.push(r.value);
399
- }
400
- return { value: items };
401
- }
402
- const rec = Object.create(null);
403
- for (const k of Object.keys(o)) {
404
- const val = o[k];
405
- if (val === undefined)
406
- continue;
407
- const r = materializeJsonFaithful(val, `${path}.${k}`, seen);
408
- if ('bad' in r)
409
- return r;
410
- Object.defineProperty(rec, k, { value: r.value, enumerable: true, writable: true, configurable: true });
411
- }
412
- if (!isPlainRecord(o))
413
- return { bad: `${path}(${shapeTag(o)})` };
414
- return { value: rec };
415
- }
416
- finally {
417
- seen.delete(o);
418
- }
419
- };
420
409
  const resolvedSnapshotForWire = (resolved) => {
421
410
  if (resolved === undefined || resolved === null)
422
411
  return {};
@@ -461,6 +450,58 @@ const resolvedSnapshotForWire = (resolved) => {
461
450
  export function hostTurnSettings(input) {
462
451
  return buildHostTurnSettings(input, { materialize: (snapshot) => resolvedSnapshotForWire(snapshot), memoryOffDeclared });
463
452
  }
453
+ const fileRestoreRequested = (restoreFiles, rewindFilesTo, rewindFiles) => restoreFiles === true || (typeof rewindFilesTo === 'string' && rewindFilesTo.length > 0) || rewindFiles === true;
454
+ function tightenedRequestOf(request, intake, lane) {
455
+ const readData = (key) => {
456
+ try {
457
+ if (!hasOwn(request, key))
458
+ return { ok: true, value: undefined };
459
+ const d = Object.getOwnPropertyDescriptor(request, key);
460
+ if (d === undefined)
461
+ return { ok: true, value: undefined };
462
+ if (!Object.hasOwn(d, 'value'))
463
+ return { ok: false };
464
+ if (d.enumerable !== true)
465
+ return { ok: true, value: undefined };
466
+ const got = request[key];
467
+ return got === d.value ? { ok: true, value: d.value } : { ok: false };
468
+ }
469
+ catch {
470
+ return { ok: false };
471
+ }
472
+ };
473
+ const unjudged = { kind: 'refuse', notice: hostProjectionNotice() };
474
+ const add = {};
475
+ const tightened = [];
476
+ if (intake.carried.memory === MEMORY_OFF && hostRefuseRowSeated('memory', lane, true)) {
477
+ const sent = readData('memory');
478
+ if (!sent.ok)
479
+ return unjudged;
480
+ if (sent.value !== MEMORY_OFF) {
481
+ add.memory = MEMORY_OFF;
482
+ appendOwnElement(tightened, 'memory');
483
+ }
484
+ }
485
+ if (intake.carried.fileHistory === FILE_HISTORY_DISABLED && hostRefuseRowSeated('fileHistory', lane, true)) {
486
+ const sent = readData('fileHistory');
487
+ if (!sent.ok)
488
+ return unjudged;
489
+ if (sent.value !== FILE_HISTORY_DISABLED) {
490
+ const restoreFiles = readData('restoreFiles');
491
+ const rewindFilesTo = readData('rewindFilesTo');
492
+ const rewindFiles = readData('rewindFiles');
493
+ if (!restoreFiles.ok || !rewindFilesTo.ok || !rewindFiles.ok)
494
+ return unjudged;
495
+ if (fileRestoreRequested(restoreFiles.value, rewindFilesTo.value, rewindFiles.value))
496
+ return { kind: 'refuse', notice: hostFileHistoryRestoreNotice() };
497
+ add.fileHistory = FILE_HISTORY_DISABLED;
498
+ appendOwnElement(tightened, 'fileHistory');
499
+ }
500
+ }
501
+ if (tightened.length === 0)
502
+ return { kind: 'ok', request, tightened: Object.freeze([]) };
503
+ return { kind: 'ok', request: { ...request, ...add }, tightened: Object.freeze(tightened) };
504
+ }
464
505
  export function refilterHostSettings(request, fresh, lane) {
465
506
  refuseBadLane(lane);
466
507
  const host = hostTurnSettingsOf(fresh);
@@ -470,16 +511,21 @@ export function refilterHostSettings(request, fresh, lane) {
470
511
  }
471
512
  const refuseNotice = () => intake.notice ?? hostProjectionNotice();
472
513
  if (intake.refuses) {
473
- const refusing = hostRefusalNoticeOf(intake, lane, true);
514
+ const refusing = hostRefusalNoticeOf(intake, lane, true, undefined);
474
515
  if (refusing !== undefined)
475
516
  return Object.freeze({ kind: 'refuse', notice: refusing });
476
517
  }
477
- const settings = request.settings;
518
+ const tight = tightenedRequestOf(request, intake, lane);
519
+ if (tight.kind === 'refuse')
520
+ return Object.freeze({ kind: 'refuse', notice: tight.notice });
521
+ const base = tight.request;
522
+ const tightened = tight.tightened;
523
+ const settings = base.settings;
478
524
  if (!isPlainRecord(settings) || !Object.hasOwn(settings, 'hooks'))
479
- return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
525
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
480
526
  const disposition = intake.dispositions['settings.hooks'];
481
527
  if (disposition === 'unwired')
482
- return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
528
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
483
529
  const now = intake.carried.hooks;
484
530
  const sentHooks = settings.hooks;
485
531
  if (!isPlainRecord(sentHooks))
@@ -488,14 +534,14 @@ export function refilterHostSettings(request, fresh, lane) {
488
534
  if (!cut.judged)
489
535
  return Object.freeze({ kind: 'refuse', notice: refuseNotice() });
490
536
  if (cut.dropped === 0)
491
- return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
537
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
492
538
  const { hooks: _sentHooks, ...restSettings } = settings;
493
539
  void _sentHooks;
494
540
  const nextSettings = cut.hooks === undefined ? restSettings : { ...restSettings, hooks: cut.hooks };
495
- const { settings: _sentSettings, ...restRequest } = request;
541
+ const { settings: _sentSettings, ...restRequest } = base;
496
542
  void _sentSettings;
497
543
  const next = Object.keys(nextSettings).length > 0 ? { ...restRequest, settings: nextSettings } : restRequest;
498
- return Object.freeze({ kind: 'send', request: next, hooksDropped: cut.dropped });
544
+ return Object.freeze({ kind: 'send', request: next, hooksDropped: cut.dropped, tightened });
499
545
  }
500
546
  export const MEMORY_CAPTURE_OFF = 'off';
501
547
  export function memoryCaptureDeclarationField(declared) {
@@ -540,7 +586,7 @@ const withoutRetiredAttachmentsKeys = (v) => {
540
586
  const d = Object.getOwnPropertyDescriptor(v, key);
541
587
  if (d !== undefined && Object.prototype.hasOwnProperty.call(d, 'value') && d.value === undefined) {
542
588
  ;
543
- (drop ??= []).push(key);
589
+ appendOwnElement((drop ??= []), key);
544
590
  continue;
545
591
  }
546
592
  throw new AttachmentsSpecRefused(`buildTaskRequest: refusing attachments key ${JSON.stringify(key)} — ${why}`, { token: key, why, allowed: ATTACHMENTS_SPEC_KEYS, key });
@@ -559,7 +605,7 @@ function allowToolsSnapshot(v) {
559
605
  return v;
560
606
  let n;
561
607
  try {
562
- n = Object.getOwnPropertyDescriptor(v, 'length')?.value;
608
+ n = readOwnDataValue(v, 'length');
563
609
  }
564
610
  catch {
565
611
  return ALLOW_TOOLS_UNREADABLE_ITEM;
@@ -569,27 +615,31 @@ function allowToolsSnapshot(v) {
569
615
  const out = new Array(n);
570
616
  for (let k = 0; k < n; k += 1) {
571
617
  let d;
618
+ let unreadable = false;
572
619
  try {
573
620
  d = Object.getOwnPropertyDescriptor(v, k);
574
621
  }
575
622
  catch {
576
- out[k] = ALLOW_TOOLS_UNREADABLE_ITEM;
577
- continue;
623
+ unreadable = true;
578
624
  }
579
- if (d === undefined)
580
- continue;
581
- out[k] = 'value' in d ? d.value : ALLOW_TOOLS_UNREADABLE_ITEM;
625
+ if (unreadable)
626
+ defineOwnIndex(out, k, ALLOW_TOOLS_UNREADABLE_ITEM);
627
+ else if (d !== undefined)
628
+ defineOwnIndex(out, k, Object.hasOwn(d, 'value') ? d.value : ALLOW_TOOLS_UNREADABLE_ITEM);
582
629
  }
583
630
  return Object.freeze(out);
584
631
  }
632
+ const defineOwnIndex = (list, k, value) => {
633
+ Object.defineProperty(list, k, { value, writable: true, enumerable: true, configurable: true });
634
+ };
585
635
  const allowToolsCopy = (v) => {
586
636
  if (!Array.isArray(v))
587
637
  return [];
588
638
  const out = [];
589
639
  for (let k = 0; k < v.length; k += 1) {
590
640
  const d = Object.getOwnPropertyDescriptor(v, k);
591
- if (d !== undefined && 'value' in d && typeof d.value === 'string')
592
- out.push(d.value);
641
+ if (d !== undefined && Object.hasOwn(d, 'value') && typeof d.value === 'string')
642
+ appendOwnElement(out, d.value);
593
643
  }
594
644
  return out;
595
645
  };
@@ -600,7 +650,7 @@ const refuseBadAllowTools = (v) => {
600
650
  let ok = true;
601
651
  for (let k = 0; k < v.length; k += 1) {
602
652
  const d = Object.getOwnPropertyDescriptor(v, k);
603
- if (d === undefined || !('value' in d) || typeof d.value !== 'string') {
653
+ if (d === undefined || !Object.hasOwn(d, 'value') || typeof d.value !== 'string') {
604
654
  ok = false;
605
655
  break;
606
656
  }
@@ -619,7 +669,7 @@ const refuseBadUserInvokedSkills = (v) => {
619
669
  let ok = true;
620
670
  for (let k = 0; k < v.length; k += 1) {
621
671
  const d = Object.getOwnPropertyDescriptor(v, k);
622
- if (d === undefined || !('value' in d) || typeof d.value !== 'string') {
672
+ if (d === undefined || !Object.hasOwn(d, 'value') || typeof d.value !== 'string') {
623
673
  ok = false;
624
674
  break;
625
675
  }
@@ -645,6 +695,24 @@ const refuseBadInheritEnv = (v) => {
645
695
  '这一位是「模型驱动的宿主 shell 继承多少用户环境」的两词闭集声明,上游对词表外的值响亮拒;环境变量名单不是请求值。' +
646
696
  '本层若静默删键,这一次运行会按缺省清洗跑、还清掉本会话上一条声明,而声明人不会知道。错误文本只报形状,不回显值。');
647
697
  };
698
+ const refuseBadProjectMemory = (v) => {
699
+ if (v === undefined || v === null)
700
+ return;
701
+ if (v === PROJECT_MEMORY_DISABLED)
702
+ return;
703
+ throw new TypeError(`buildTaskRequest: projectMemory 只接受字面量 '${PROJECT_MEMORY_DISABLED}' 或缺席(undefined/null)—— 收到 ${coarseShape(v)}。` +
704
+ '这一位是「这一次运行别读部署的项目上下文」的收紧声明,引擎是**单成员闭集**(没有第二个词:要照常读就不带这一键)且对坏值拒启。' +
705
+ '本层若静默删键,请求会照发、项目指令文件会照常递给模型,而声明人不会知道自己的 opt-out 丢了。错误文本只报形状,不回显值。');
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
+ };
648
716
  const refuseBadRequireExistingSession = (v) => {
649
717
  if (v === undefined || v === null || v === true || v === false)
650
718
  return;
@@ -733,7 +801,7 @@ export function parseMaxCostUsd(text) {
733
801
  return { ok: false, why: 'not_positive' };
734
802
  return { ok: true, value };
735
803
  }
736
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills'];
804
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills', 'projectMemory', 'thinkingLatch'];
737
805
  const MATERIALIZE_AT_READ = Object.freeze({ allowTools: (v) => allowToolsSnapshot(v), userInvokedSkills: (v) => allowToolsSnapshot(v) });
738
806
  function dataFirstSnapshot(input, afterData) {
739
807
  let descs;
@@ -749,10 +817,10 @@ function dataFirstSnapshot(input, afterData) {
749
817
  const d = descs[k];
750
818
  if (d === undefined || (d.enumerable !== true && routeOfTopLevelKey(k) === undefined))
751
819
  continue;
752
- if ('value' in d)
820
+ if (Object.hasOwn(d, 'value'))
753
821
  bag[k] = Object.hasOwn(MATERIALIZE_AT_READ, k) ? MATERIALIZE_AT_READ[k](d.value) : d.value;
754
822
  else
755
- accessors.push(k);
823
+ appendOwnElement(accessors, k);
756
824
  }
757
825
  afterData?.(bag);
758
826
  const ordered = [
@@ -765,8 +833,9 @@ function dataFirstSnapshot(input, afterData) {
765
833
  }
766
834
  return bag;
767
835
  }
768
- export function assembleTaskRequest(input, lane) {
769
- refuseBadLane(lane);
836
+ export function assembleTaskRequest(input, laneOrIdentity) {
837
+ const { lane, origin } = laneAxesOf(laneOrIdentity, 'assembly');
838
+ refuseOriginAxisDrift();
770
839
  let resolvedInEarly;
771
840
  let resolvedMaterialized;
772
841
  let resolvedMaterializeError;
@@ -809,7 +878,7 @@ export function assembleTaskRequest(input, lane) {
809
878
  throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(hostIn) })));
810
879
  const resolvedIn = resolvedInEarly;
811
880
  const resolvedRoute = SETTINGS_KEY_ROUTES.resolved;
812
- const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live) === undefined;
881
+ const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live, origin) === undefined;
813
882
  if (resolvedOpen && resolvedMaterializeError !== undefined)
814
883
  throw resolvedMaterializeError;
815
884
  const resolvedWire = resolvedOpen ? (resolvedMaterialized ?? resolvedSnapshotForWire(resolvedIn)) : {};
@@ -822,6 +891,8 @@ export function assembleTaskRequest(input, lane) {
822
891
  const disableSkillsIn = src.disableSkills;
823
892
  const approvalReachIn = src.approvalReach;
824
893
  const allowToolsIn = src.allowTools;
894
+ const projectMemoryIn = src.projectMemory;
895
+ const thinkingLatchIn = src.thinkingLatch;
825
896
  const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
826
897
  const i = snapshotByName(src, INPUT_KEYS, {
827
898
  live,
@@ -835,17 +906,25 @@ export function assembleTaskRequest(input, lane) {
835
906
  disableSkills: disableSkillsIn,
836
907
  approvalReach: approvalReachIn,
837
908
  allowTools: allowToolsIn,
909
+ projectMemory: projectMemoryIn,
910
+ thinkingLatch: thinkingLatchIn,
838
911
  settings: settingsIn,
839
912
  });
840
913
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
841
914
  const hostSettingsKeysIn = HOST_SETTINGS_SUBKEYS.filter((k) => hasOwn(settingsObj, k) && present(Reflect.get(settingsObj, k)));
842
915
  const hostTopKeysIn = HOST_TOP_KEYS.filter((k) => hasOwn(src, k) && present(Reflect.get(src, k)));
916
+ const approvalFamiliesReadingIn = i.approvalFrameFamiliesReading;
917
+ const familiesJudged = present(approvalFamiliesReadingIn)
918
+ ? frameFamiliesJudgementOf(approvalFamiliesReadingIn)
919
+ : undefined;
920
+ const approvalFamilies = familiesJudged?.ok === true ? familiesJudged.families : undefined;
843
921
  const s = {
844
922
  resolved: resolvedIn,
845
923
  webSearch: intake.carried.webSearch,
846
924
  ultracode: snapshotByName(settingsObj, ['ultracode']).ultracode,
847
925
  hooks: intake.carried.hooks,
848
926
  outputStyle: intake.carried.outputStyle,
927
+ approvalFrameFamilies: approvalFamilies,
849
928
  };
850
929
  refuseBadMemoryCapture(memoryCaptureIn);
851
930
  refuseBadMaxCostUsd(maxCostUsdIn);
@@ -857,6 +936,8 @@ export function assembleTaskRequest(input, lane) {
857
936
  refuseBadDisableSkills(disableSkillsIn, 'buildTaskRequest');
858
937
  refuseBadApprovalReach(approvalReachIn, 'buildTaskRequest');
859
938
  refuseBadAllowTools(allowToolsIn);
939
+ refuseBadProjectMemory(projectMemoryIn);
940
+ refuseBadThinkingLatch(thinkingLatchIn);
860
941
  refuseBadUserInvokedSkills(i.userInvokedSkills);
861
942
  const rewindRoute = INPUT_KEY_ROUTES.rewind;
862
943
  const RETIRED_REWIND_FILES_KEY = 'rewindFiles';
@@ -878,7 +959,7 @@ export function assembleTaskRequest(input, lane) {
878
959
  const rewindForGate = present(rewindIn) ? rewindWire : rewindIn;
879
960
  const omitted = [];
880
961
  const omit = (key, cause) => {
881
- omitted.push(Object.freeze({ key, cause }));
962
+ appendOwnElement(omitted, Object.freeze({ key, cause }));
882
963
  };
883
964
  const unknownKeys = [];
884
965
  const lateOmitted = [];
@@ -892,22 +973,22 @@ export function assembleTaskRequest(input, lane) {
892
973
  continue;
893
974
  const route = routeOfTopLevelKey(key);
894
975
  if (route === undefined) {
895
- unknownKeys.push(key);
976
+ appendOwnElement(unknownKeys, key);
896
977
  continue;
897
978
  }
898
979
  if (route.via !== 'matrix' && route.via !== 'fixed')
899
980
  continue;
900
- const cause = omissionCauseOf(standingOf(route), lane, live);
981
+ const cause = omissionCauseOf(standingOf(route), lane, live, origin);
901
982
  if (cause !== undefined)
902
- lateOmitted.push(Object.freeze({ key, cause }));
983
+ appendOwnElement(lateOmitted, Object.freeze({ key, cause }));
903
984
  }
904
985
  for (const sub of Object.keys(settingsObj)) {
905
- if (hasOwn(SETTINGS_KEY_ROUTES, sub))
986
+ if (hasOwn(SETTINGS_KEY_ROUTES, sub) && !MINTED_SETTINGS_SUBKEYS.includes(sub))
906
987
  continue;
907
988
  const stray = Reflect.get(settingsObj, sub);
908
989
  if (!present(stray))
909
990
  continue;
910
- unknownKeys.push(`settings.${sub}`);
991
+ appendOwnElement(unknownKeys, `settings.${sub}`);
911
992
  }
912
993
  let retiredRewindFiles = false;
913
994
  for (const sub of Object.keys(rewindObj)) {
@@ -918,7 +999,7 @@ export function assembleTaskRequest(input, lane) {
918
999
  continue;
919
1000
  if (sub === RETIRED_REWIND_FILES_KEY)
920
1001
  retiredRewindFiles = true;
921
- unknownKeys.push(`rewind.${sub}`);
1002
+ appendOwnElement(unknownKeys, `rewind.${sub}`);
922
1003
  }
923
1004
  if (retiredRewindFiles) {
924
1005
  throw new TypeError('buildTaskRequest: "rewind.rewindFiles" 是退役键(引擎 core 6.0.0 起退役:「逐轮捕获」那一义被引擎忽略 —— 文件首触追踪恒开,' +
@@ -943,19 +1024,19 @@ export function assembleTaskRequest(input, lane) {
943
1024
  '错误文本只报键名,不回显值。');
944
1025
  }
945
1026
  refuseRequireExistingWithoutSession(requireExistingSessionIn, i.sessionId);
946
- refuseDisableSkillsConflict(disableSkillsIn, i.skills, Reflect.get(src, 'agents'), 'buildTaskRequest');
1027
+ refuseDisableSkillsConflict(disableSkillsIn, i.skills, Reflect.get(src, 'agents'), 'buildTaskRequest', i.userInvokedSkills);
947
1028
  refuseUtilityWithoutToolUnload(lane, excludeAllToolsIn);
948
1029
  if (intake.refuses) {
949
- const refusing = hostRefusalNoticeOf(intake, lane, live);
1030
+ const refusing = hostRefusalNoticeOf(intake, lane, live, origin);
950
1031
  if (refusing !== undefined)
951
- throw new HostSettingsRefused(refusing);
1032
+ throw hostSettingsRefusedError(refusing, intake, intake.refusingKeys.find((k) => hostRefuseRowSeated(k, lane, live, origin)));
952
1033
  }
953
1034
  const carries = (route, value, key) => {
954
1035
  if (route.via === 'always')
955
1036
  return present(value);
956
1037
  if (route.via === 'control' || route.via === 'settings')
957
1038
  return false;
958
- const cause = omissionCauseOf(standingOf(route), lane, live);
1039
+ const cause = seatCauseOf(route, lane, live, origin);
959
1040
  if (!inPlay(key, value))
960
1041
  return false;
961
1042
  if (cause === undefined)
@@ -965,6 +1046,8 @@ export function assembleTaskRequest(input, lane) {
965
1046
  };
966
1047
  const gate = Object.create(null);
967
1048
  const askInput = (key) => {
1049
+ if (key === THINKING_DEFERRED_ASK)
1050
+ return;
968
1051
  const route = INPUT_KEY_ROUTES[key];
969
1052
  if (route.via === 'settings') {
970
1053
  for (const sub of SETTINGS_KEYS)
@@ -983,14 +1066,23 @@ export function assembleTaskRequest(input, lane) {
983
1066
  };
984
1067
  for (const key of INPUT_KEYS.slice(hostTopAt))
985
1068
  askInput(key);
986
- const on = (key) => gate[key] === true;
987
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;
988
1079
  const settingsOut = {
989
1080
  ...resolvedWire,
990
1081
  ...(onSetting('webSearch') ? { webSearch: s.webSearch } : {}),
991
1082
  ...(onSetting('ultracode') ? { ultracode: s.ultracode } : {}),
992
1083
  ...(onSetting('hooks') ? { hooks: s.hooks } : {}),
993
1084
  ...(s.outputStyle !== undefined ? { outputStyle: s.outputStyle } : {}),
1085
+ ...(onSetting('approvalFrameFamilies') ? { approvalFrameFamilies: [...(s.approvalFrameFamilies ?? [])] } : {}),
994
1086
  };
995
1087
  const attachmentsForWire = on('attachments') ? withoutRetiredAttachmentsKeys(i.attachments) : undefined;
996
1088
  const out = {
@@ -998,7 +1090,7 @@ export function assembleTaskRequest(input, lane) {
998
1090
  sessionId: i.sessionId,
999
1091
  ...(on('scenario') ? { scenario: i.scenario } : {}),
1000
1092
  ...(on('systemPrompt') ? { systemPrompt: i.systemPrompt } : {}),
1001
- ...(on('reasoningEffort') ? { reasoningEffort: i.reasoningEffort } : {}),
1093
+ ...(on('reasoningEffort') ? { reasoningEffort: thinking.word } : {}),
1002
1094
  ...(on('model') ? { model: i.model } : {}),
1003
1095
  ...(on('images') ? { images: i.images } : {}),
1004
1096
  ...(on('userAttachments') ? { userAttachments: [...(i.userAttachments ?? [])] } : {}),
@@ -1036,12 +1128,105 @@ export function assembleTaskRequest(input, lane) {
1036
1128
  ...(on('requireExistingSession') ? { requireExistingSession: true } : {}),
1037
1129
  ...(on('disableSkills') ? { disableSkills: true } : {}),
1038
1130
  ...(on('approvalReach') ? { approvalReach: approvalReachIn } : {}),
1131
+ ...(on('projectMemory') ? { projectMemory: PROJECT_MEMORY_DISABLED } : {}),
1039
1132
  ...(Object.keys(settingsOut).length > 0 ? { settings: settingsOut } : {}),
1040
1133
  };
1041
- return Object.freeze({ request: out, omitted: Object.freeze([...omitted, ...lateOmitted]), host });
1134
+ const familiesRoute = SETTINGS_KEY_ROUTES.approvalFrameFamilies;
1135
+ const familiesSeated = (familiesRoute.via === 'matrix' || familiesRoute.via === 'fixed') && omissionCauseOf(standingOf(familiesRoute), lane, live, origin) === undefined;
1136
+ const familiesWithheld = familiesSeated && approvalFamilies === undefined ? Object.freeze({ cause: familiesJudged === undefined || familiesJudged.ok ? 'reading_absent' : familiesJudged.cause }) : undefined;
1137
+ return Object.freeze({
1138
+ request: out,
1139
+ omitted: Object.freeze([...omitted, ...lateOmitted]),
1140
+ host,
1141
+ ...(familiesWithheld !== undefined ? { approvalFrameFamiliesWithheld: familiesWithheld } : {}),
1142
+ thinking,
1143
+ });
1144
+ }
1145
+ export function buildTaskRequest(input, laneOrIdentity) {
1146
+ return assembleTaskRequest(input, laneOrIdentity).request;
1042
1147
  }
1043
- export function buildTaskRequest(input, lane) {
1044
- return assembleTaskRequest(input, lane).request;
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 });
1045
1230
  }
1046
1231
  export function applyLiveRequestDefaults(req, host) {
1047
1232
  const out = { ...req };
@@ -1059,7 +1244,7 @@ export function applyLiveRequestDefaults(req, host) {
1059
1244
  out.retainSubagentSessions = true;
1060
1245
  if (out.agents === undefined && host.agents !== undefined)
1061
1246
  out.agents = host.agents;
1062
- refuseDisableSkillsConflict(out.disableSkills, out.skills, out.agents, 'applyLiveRequestDefaults');
1247
+ refuseDisableSkillsConflict(out.disableSkills, out.skills, out.agents, 'applyLiveRequestDefaults', Array.isArray(out.userInvokedSkills) ? out.userInvokedSkills : undefined);
1063
1248
  if (typeof out.scenario === 'string' && host.mockScenarioKeys?.has(out.scenario)) {
1064
1249
  delete out.scenario;
1065
1250
  }
@@ -1116,25 +1301,25 @@ export function unregisteredRequestKeys(req, lane) {
1116
1301
  if (settings === null || typeof settings !== 'object')
1117
1302
  continue;
1118
1303
  if (typeof settings.toJSON === 'function')
1119
- out.push('settings.toJSON');
1304
+ appendOwnElement(out, 'settings.toJSON');
1120
1305
  for (const [sub, value] of Object.entries(settings)) {
1121
1306
  if (sub === 'toJSON')
1122
1307
  continue;
1123
1308
  if (typeof value === 'function') {
1124
- out.push(`settings.${sub}`);
1309
+ appendOwnElement(out, `settings.${sub}`);
1125
1310
  continue;
1126
1311
  }
1127
1312
  if (laneSettingsKeys.has(sub) || liveDefaults.has(`settings.${sub}`))
1128
1313
  continue;
1129
1314
  if (settingsIsOpenSet && !foreignSettingsKeys.has(sub))
1130
1315
  continue;
1131
- out.push(`settings.${sub}`);
1316
+ appendOwnElement(out, `settings.${sub}`);
1132
1317
  }
1133
1318
  continue;
1134
1319
  }
1135
1320
  if (laneTopKeys.has(key) || liveDefaults.has(key))
1136
1321
  continue;
1137
- out.push(key);
1322
+ appendOwnElement(out, key);
1138
1323
  }
1139
1324
  return out;
1140
1325
  }