@sema-agent/client-core 0.99.0 → 0.101.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 (374) hide show
  1. package/CHANGELOG.md +572 -0
  2. package/README.md +174 -114
  3. package/dist/adapt/arms.js +109 -50
  4. package/dist/adapt/panelTasks.d.ts +2 -1
  5. package/dist/adapt/panelTasks.js +32 -9
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +58 -32
  8. package/dist/adapt/toolCards.js +9 -8
  9. package/dist/adapt.js +7 -1
  10. package/dist/adapter/activeRunSelfHeal.d.ts +31 -2
  11. package/dist/adapter/activeRunSelfHeal.js +162 -13
  12. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  13. package/dist/adapter/downstream/eventToSdkMessage.js +157 -20
  14. package/dist/adapter/downstream/terminalToSdkResult.d.ts +60 -1
  15. package/dist/adapter/downstream/terminalToSdkResult.js +219 -39
  16. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  17. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  18. package/dist/adapter/runStream.d.ts +4 -4
  19. package/dist/adapter/runStream.js +184 -42
  20. package/dist/adapter/types.d.ts +2 -0
  21. package/dist/agentsWireCaps.d.ts +9 -7
  22. package/dist/agentsWireCaps.js +101 -17
  23. package/dist/approvalDecidedByCapability.js +2 -2
  24. package/dist/approvalFrameFamilies.d.ts +33 -0
  25. package/dist/approvalFrameFamilies.js +120 -0
  26. package/dist/approvalsStreamLiveCapability.js +2 -2
  27. package/dist/attachmentsWireCaps.js +41 -13
  28. package/dist/callerCwdCapability.js +2 -2
  29. package/dist/centerWiringCapability.js +2 -2
  30. package/dist/clockReading.d.ts +2 -0
  31. package/dist/clockReading.js +4 -0
  32. package/dist/cloudConfigWireCaps.d.ts +28 -5
  33. package/dist/cloudConfigWireCaps.js +334 -103
  34. package/dist/compactionStatus.d.ts +34 -0
  35. package/dist/compactionStatus.js +196 -0
  36. package/dist/coreValuePorts.d.ts +12 -2
  37. package/dist/coreValuePorts.js +9 -0
  38. package/dist/decideFailureNote.js +8 -6
  39. package/dist/decideReceipt.js +2 -2
  40. package/dist/deviceExecutorManagementCapability.js +2 -2
  41. package/dist/deviceFlowPolling.js +1 -1
  42. package/dist/disableSkillsWire.d.ts +1 -1
  43. package/dist/disableSkillsWire.js +4 -3
  44. package/dist/displayUntrusted.d.ts +1 -0
  45. package/dist/displayUntrusted.js +215 -13
  46. package/dist/engineAgentPanelStore.d.ts +7 -4
  47. package/dist/engineCapReader.js +1 -1
  48. package/dist/engineConfigDrift.js +1 -1
  49. package/dist/engineErrorCodes.d.ts +23 -6
  50. package/dist/engineErrorCodes.js +36 -11
  51. package/dist/engineGenerationConvergence.d.ts +39 -0
  52. package/dist/engineGenerationConvergence.js +95 -0
  53. package/dist/engineGenerationPort.d.ts +61 -0
  54. package/dist/engineGenerationPort.js +144 -0
  55. package/dist/engineHealthFacts.d.ts +23 -0
  56. package/dist/engineHealthFacts.js +92 -0
  57. package/dist/engineIdentity.d.ts +4 -0
  58. package/dist/engineIdentity.js +11 -0
  59. package/dist/engineNoticeCodes.d.ts +2 -5
  60. package/dist/engineRowRef.js +4 -0
  61. package/dist/engineSessionMessages.js +2 -2
  62. package/dist/engineToolDirectory.d.ts +3 -0
  63. package/dist/engineToolDirectory.js +6 -4
  64. package/dist/engineWireFor.d.ts +99 -0
  65. package/dist/engineWireFor.js +248 -6
  66. package/dist/engineWireTarget.d.ts +1 -0
  67. package/dist/engineWireTarget.js +8 -0
  68. package/dist/env/uiLanguage.js +1 -1
  69. package/dist/errorSupplementRoles.d.ts +3 -0
  70. package/dist/errorSupplementRoles.js +37 -0
  71. package/dist/eventLeg.d.ts +1 -7
  72. package/dist/eventLeg.js +7 -8
  73. package/dist/executionLaneCapability.js +2 -2
  74. package/dist/fileHistoryCaptureCapability.d.ts +0 -3
  75. package/dist/fileHistoryCaptureCapability.js +3 -7
  76. package/dist/finalVerifyWire.d.ts +6 -3
  77. package/dist/finalVerifyWire.js +6 -23
  78. package/dist/flagInput.d.ts +4 -0
  79. package/dist/flagInput.js +24 -0
  80. package/dist/fleet/fleetLedger.js +5 -2
  81. package/dist/fleet/fleetProjection.d.ts +1 -0
  82. package/dist/fleet/fleetProjection.js +15 -2
  83. package/dist/gateVocabulary.js +3 -0
  84. package/dist/generated/engineFactTables.d.ts +7 -2
  85. package/dist/generated/engineFactTables.js +79 -0
  86. package/dist/generated/engineNoticeTables.js +15 -0
  87. package/dist/generated/settingsSchemaActiveTiers.d.ts +1 -0
  88. package/dist/generated/settingsSchemaActiveTiers.js +14 -0
  89. package/dist/generated/settingsSchemaCallerTextEcho.d.ts +1 -0
  90. package/dist/generated/settingsSchemaCallerTextEcho.js +8 -0
  91. package/dist/generated/settingsSchemaControlAndBidi.d.ts +1 -0
  92. package/dist/generated/settingsSchemaControlAndBidi.js +8 -0
  93. package/dist/generated/settingsSchemaDefaultModel.d.ts +11 -0
  94. package/dist/generated/settingsSchemaDefaultModel.js +25 -0
  95. package/dist/generated/settingsSchemaFns.d.ts +66 -0
  96. package/dist/generated/settingsSchemaFns.js +38 -0
  97. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  98. package/dist/generated/settingsSchemaMcpRequestEntry.js +156 -0
  99. package/dist/generated/settingsSchemaSafetyMergeSpec.d.ts +1 -0
  100. package/dist/generated/settingsSchemaSafetyMergeSpec.js +95 -0
  101. package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -0
  102. package/dist/generated/settingsSchemaSettingsSources.js +58 -0
  103. package/dist/generated/settingsSchemaTables.d.ts +3 -0
  104. package/dist/generated/settingsSchemaTables.js +18 -0
  105. package/dist/generated/settingsSchemaTypes.d.ts +28 -0
  106. package/dist/generated/settingsSchemaUrlPattern.d.ts +1 -0
  107. package/dist/generated/settingsSchemaUrlPattern.js +183 -0
  108. package/dist/headlessPermissionModeWire.d.ts +18 -2
  109. package/dist/headlessPermissionModeWire.js +30 -19
  110. package/dist/hitl/allowRuleShadow.js +9 -7
  111. package/dist/hitl/approvalCardReading.d.ts +3 -0
  112. package/dist/hitl/approvalCardReading.js +17 -0
  113. package/dist/hitl/approvalCardReasons.js +2 -2
  114. package/dist/hitl/approvalCompleteParked.js +1 -1
  115. package/dist/hitl/approvalDelivery.d.ts +87 -0
  116. package/dist/hitl/approvalDelivery.js +193 -0
  117. package/dist/hitl/approvalHolds.d.ts +14 -0
  118. package/dist/hitl/approvalHolds.js +41 -0
  119. package/dist/hitl/approvalResolution.d.ts +11 -0
  120. package/dist/hitl/approvalResolution.js +50 -11
  121. package/dist/hitl/askClassPair.d.ts +8 -2
  122. package/dist/hitl/askClassPair.js +31 -4
  123. package/dist/hitl/askParkAutoReopenBlock.d.ts +30 -0
  124. package/dist/hitl/askParkAutoReopenBlock.js +117 -0
  125. package/dist/hitl/askParkRowRouting.js +4 -1
  126. package/dist/hitl/attributionLadder.d.ts +62 -0
  127. package/dist/hitl/attributionLadder.js +138 -0
  128. package/dist/hitl/automaticAnswerVerdict.d.ts +6 -1
  129. package/dist/hitl/automaticAnswerVerdict.js +46 -11
  130. package/dist/hitl/crashConverged.js +2 -2
  131. package/dist/hitl/decisionConflict.d.ts +6 -2
  132. package/dist/hitl/decisionConflict.js +24 -9
  133. package/dist/hitl/decisionLedger.d.ts +55 -0
  134. package/dist/hitl/decisionLedger.js +156 -0
  135. package/dist/hitl/decisionRefusal.d.ts +21 -10
  136. package/dist/hitl/decisionRefusal.js +100 -55
  137. package/dist/hitl/decisionSend.d.ts +116 -0
  138. package/dist/hitl/decisionSend.js +563 -0
  139. package/dist/hitl/frameRouter.d.ts +1 -0
  140. package/dist/hitl/frameRouter.js +9 -2
  141. package/dist/hitl/hitlBridge.d.ts +18 -6
  142. package/dist/hitl/hitlBridge.js +59 -43
  143. package/dist/hitl/livePendingAsk.js +4 -3
  144. package/dist/hitl/noAllowCause.js +1 -1
  145. package/dist/hitl/parkResolver.js +3 -2
  146. package/dist/hitl/persistedRulesWire.js +1 -0
  147. package/dist/hitl/planReviewWire.d.ts +4 -0
  148. package/dist/hitl/planReviewWire.js +104 -106
  149. package/dist/hitl/resumeRunningCard.d.ts +3 -2
  150. package/dist/hitl/resumeRunningCard.js +17 -11
  151. package/dist/hitl/sessionPolicyDeliverable.d.ts +5 -2
  152. package/dist/hitl/sessionPolicyDeliverable.js +65 -35
  153. package/dist/hitl/sessionPolicyWire.d.ts +13 -3
  154. package/dist/hitl/sessionPolicyWire.js +81 -16
  155. package/dist/hitl/standingRows.js +1 -1
  156. package/dist/hitl/toolApprovalWire.d.ts +9 -10
  157. package/dist/hitl/toolApprovalWire.js +103 -151
  158. package/dist/hookMatcherVerdict.d.ts +2 -0
  159. package/dist/hookMatcherVerdict.js +12 -0
  160. package/dist/hooksCompactTransition.d.ts +19 -0
  161. package/dist/hooksCompactTransition.js +41 -0
  162. package/dist/hooksWireCaps.d.ts +1 -1
  163. package/dist/hooksWireCaps.js +202 -203
  164. package/dist/host.d.ts +1 -14
  165. package/dist/host.js +0 -8
  166. package/dist/hostCallbackGuard.d.ts +1 -0
  167. package/dist/hostCallbackGuard.js +1 -1
  168. package/dist/hostSettingsSource.d.ts +172 -0
  169. package/dist/hostSettingsSource.js +675 -0
  170. package/dist/hostTurnSettings.d.ts +114 -19
  171. package/dist/hostTurnSettings.js +966 -68
  172. package/dist/httpHookPolicy.d.ts +37 -0
  173. package/dist/httpHookPolicy.js +176 -0
  174. package/dist/humanTurn.d.ts +4 -2
  175. package/dist/humanTurn.js +40 -10
  176. package/dist/humanTurnRead.js +4 -3
  177. package/dist/humanTurnSentences.d.ts +10 -0
  178. package/dist/humanTurnSentences.js +74 -0
  179. package/dist/i18n/messages.en.d.ts +313 -0
  180. package/dist/i18n/messages.en.js +326 -13
  181. package/dist/i18n/messages.zh.js +341 -13
  182. package/dist/i18n/translate.js +3 -3
  183. package/dist/index.d.ts +85 -15
  184. package/dist/index.js +53 -12
  185. package/dist/inheritEnvCapability.d.ts +4 -1
  186. package/dist/inheritEnvCapability.js +38 -17
  187. package/dist/inheritEnvWire.d.ts +2 -1
  188. package/dist/inheritEnvWire.js +11 -5
  189. package/dist/injectionLaneNotice.d.ts +5 -0
  190. package/dist/injectionLaneNotice.js +46 -0
  191. package/dist/interactiveToolsWire.d.ts +4 -2
  192. package/dist/interactiveToolsWire.js +5 -5
  193. package/dist/jsonFaithful.d.ts +20 -0
  194. package/dist/jsonFaithful.js +152 -0
  195. package/dist/limitsWire.d.ts +7 -2
  196. package/dist/limitsWire.js +19 -13
  197. package/dist/liveInitToolFace.d.ts +6 -1
  198. package/dist/liveInitToolFace.js +5 -3
  199. package/dist/liveModelCatalog.d.ts +22 -0
  200. package/dist/liveModelCatalog.js +132 -18
  201. package/dist/mcpPanel.js +3 -1
  202. package/dist/mcpProbeCapability.js +2 -2
  203. package/dist/mcpReconnect.js +2 -2
  204. package/dist/mcpStatusSupersetKeys.d.ts +24 -0
  205. package/dist/mcpStatusSupersetKeys.js +45 -0
  206. package/dist/mcpToolToggles.d.ts +6 -0
  207. package/dist/mcpToolToggles.js +47 -0
  208. package/dist/mcpWireCaps.d.ts +48 -4
  209. package/dist/mcpWireCaps.js +338 -99
  210. package/dist/mcpWireTransport.d.ts +4 -0
  211. package/dist/mcpWireTransport.js +18 -0
  212. package/dist/memoryComplianceCapability.js +2 -2
  213. package/dist/memoryOriginCapability.js +2 -2
  214. package/dist/model/catalogDefault.d.ts +24 -0
  215. package/dist/model/catalogDefault.js +4 -0
  216. package/dist/model/liveCatalogShape.js +5 -4
  217. package/dist/model/modelsPolicy.js +4 -3
  218. package/dist/model/onboardJudgement.d.ts +1 -0
  219. package/dist/model/onboardJudgement.js +1 -1
  220. package/dist/model/subagentModelWord.js +2 -2
  221. package/dist/modelCapabilityProbe.d.ts +2 -1
  222. package/dist/modelCapabilityProbe.js +16 -15
  223. package/dist/modelIdentityResolvability.d.ts +12 -1
  224. package/dist/modelIdentityResolvability.js +69 -28
  225. package/dist/modsKernelIdentity.d.ts +10 -0
  226. package/dist/modsKernelIdentity.js +103 -0
  227. package/dist/modsKernelTransit.d.ts +8 -0
  228. package/dist/modsKernelTransit.js +6 -0
  229. package/dist/notifications.d.ts +30 -12
  230. package/dist/notifications.js +241 -53
  231. package/dist/outputStyleSeatCapability.js +2 -2
  232. package/dist/ownKey.d.ts +37 -0
  233. package/dist/ownKey.js +238 -2
  234. package/dist/panelReduce.d.ts +5 -0
  235. package/dist/panelReduce.js +45 -13
  236. package/dist/panelStateHolder.d.ts +1 -0
  237. package/dist/panelStateHolder.js +10 -1
  238. package/dist/peerFrames.d.ts +1 -1
  239. package/dist/peerFrames.js +6 -2
  240. package/dist/peerLaneCapability.js +2 -2
  241. package/dist/permissionModesCapability.js +4 -4
  242. package/dist/permissionRuleIssue.d.ts +2 -1
  243. package/dist/permissionRuleIssue.js +24 -21
  244. package/dist/permissionRulesWriteCapability.js +2 -2
  245. package/dist/permissionsAllowGrantCapability.js +2 -2
  246. package/dist/permissionsDeclaration.d.ts +43 -4
  247. package/dist/permissionsDeclaration.js +102 -35
  248. package/dist/pluginHooksWire.d.ts +20 -4
  249. package/dist/pluginHooksWire.js +101 -60
  250. package/dist/prepareRefusal.d.ts +9 -0
  251. package/dist/prepareRefusal.js +90 -0
  252. package/dist/printInitToolFace.js +2 -2
  253. package/dist/printModelStatus.js +1 -1
  254. package/dist/projectMemory.d.ts +32 -0
  255. package/dist/projectMemory.js +70 -0
  256. package/dist/promptBlockedCopy.d.ts +2 -1
  257. package/dist/promptBlockedCopy.js +2 -1
  258. package/dist/promptDelivery.d.ts +23 -3
  259. package/dist/promptDelivery.js +169 -30
  260. package/dist/providerRejection.d.ts +19 -1
  261. package/dist/providerRejection.js +75 -4
  262. package/dist/queuedDelivery.d.ts +48 -0
  263. package/dist/queuedDelivery.js +233 -0
  264. package/dist/reattachWindow.d.ts +94 -0
  265. package/dist/reattachWindow.js +127 -0
  266. package/dist/request/printNotification.d.ts +1 -1
  267. package/dist/request/printNotification.js +5 -1
  268. package/dist/request/submissionIdentity.d.ts +37 -0
  269. package/dist/request/submissionIdentity.js +65 -0
  270. package/dist/request/taskRequest.d.ts +22 -14
  271. package/dist/request/taskRequest.js +312 -202
  272. package/dist/resultFrameKeys.d.ts +1 -0
  273. package/dist/resultFrameKeys.js +4 -1
  274. package/dist/resumeRefusalCopy.d.ts +3 -1
  275. package/dist/resumeRefusalCopy.js +22 -4
  276. package/dist/retryStatus.d.ts +26 -0
  277. package/dist/retryStatus.js +133 -28
  278. package/dist/rewindArchiveCapability.js +2 -2
  279. package/dist/rewindEngineAction.d.ts +2 -0
  280. package/dist/rewindEngineAction.js +23 -1
  281. package/dist/rowKind.d.ts +36 -0
  282. package/dist/rowKind.js +97 -0
  283. package/dist/ruleCompileVerdict.d.ts +18 -0
  284. package/dist/ruleCompileVerdict.js +67 -2
  285. package/dist/ruleReadings.d.ts +58 -0
  286. package/dist/ruleReadings.js +132 -0
  287. package/dist/ruleRefusalSentence.d.ts +8 -0
  288. package/dist/ruleRefusalSentence.js +64 -0
  289. package/dist/runCancelContext.d.ts +0 -1
  290. package/dist/runCancelContext.js +1 -2
  291. package/dist/runTerminal.d.ts +5 -0
  292. package/dist/runTerminal.js +52 -17
  293. package/dist/sandboxWire.d.ts +3 -2
  294. package/dist/sandboxWire.js +59 -27
  295. package/dist/scenarioRefusal.d.ts +2 -1
  296. package/dist/scenarioRefusal.js +6 -6
  297. package/dist/scenarioSkills.d.ts +20 -0
  298. package/dist/scenarioSkills.js +72 -0
  299. package/dist/scenarioWire.d.ts +5 -2
  300. package/dist/scenarioWire.js +9 -9
  301. package/dist/sealedKeyCapability.js +2 -2
  302. package/dist/seam.d.ts +26 -9
  303. package/dist/seam.js +44 -4
  304. package/dist/seatContract.d.ts +62 -9
  305. package/dist/seatContract.js +70 -25
  306. package/dist/selfOrchestrationDenial.js +1 -1
  307. package/dist/sessionPolicyCapability.js +2 -2
  308. package/dist/sessionRulesChildrenReadLive.d.ts +6 -0
  309. package/dist/sessionRulesChildrenReadLive.js +5 -0
  310. package/dist/sessionRulesNamespaceFormsCapability.d.ts +12 -0
  311. package/dist/sessionRulesNamespaceFormsCapability.js +29 -0
  312. package/dist/sessionSyncFileHistoryCapability.js +2 -2
  313. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  314. package/dist/settingsCredentialKeyPaths.js +274 -0
  315. package/dist/settingsHooksSalvage.d.ts +1 -0
  316. package/dist/settingsHooksSalvage.js +7 -1
  317. package/dist/settingsLayerAdmission.d.ts +4 -0
  318. package/dist/settingsLayerAdmission.js +16 -0
  319. package/dist/skillToolListsCapability.js +6 -5
  320. package/dist/skillsWireCaps.d.ts +14 -3
  321. package/dist/skillsWireCaps.js +48 -18
  322. package/dist/slashCommandGate.d.ts +24 -0
  323. package/dist/slashCommandGate.js +74 -0
  324. package/dist/slashCommandTable.d.ts +14 -0
  325. package/dist/slashCommandTable.js +99 -0
  326. package/dist/sqlEngineCapability.js +2 -2
  327. package/dist/steerPriorityCapability.js +2 -2
  328. package/dist/storePostureCapability.js +2 -2
  329. package/dist/subagent/agentStateDetail.js +1 -1
  330. package/dist/subagent/engineDelegatedPrompt.d.ts +9 -0
  331. package/dist/subagent/engineDelegatedPrompt.js +38 -7
  332. package/dist/subagent/engineRowStopGate.js +2 -2
  333. package/dist/subagent/engineSubagentResume.js +3 -3
  334. package/dist/subagent/engineTaskHandleWire.d.ts +1 -0
  335. package/dist/subagent/engineTaskHandleWire.js +10 -0
  336. package/dist/subagentContentStore.d.ts +1 -0
  337. package/dist/subagentContentStore.js +135 -27
  338. package/dist/submitFailure.d.ts +8 -0
  339. package/dist/submitFailure.js +17 -0
  340. package/dist/supportFloor.d.ts +1 -0
  341. package/dist/supportFloor.js +7 -0
  342. package/dist/terminationIntent.d.ts +55 -0
  343. package/dist/terminationIntent.js +352 -0
  344. package/dist/toolHistoryMismatch.d.ts +8 -0
  345. package/dist/toolHistoryMismatch.js +37 -6
  346. package/dist/toolResult.d.ts +13 -3
  347. package/dist/toolResult.js +158 -9
  348. package/dist/toolResultContent.d.ts +13 -0
  349. package/dist/toolResultContent.js +82 -7
  350. package/dist/toolRoster.d.ts +18 -0
  351. package/dist/toolRoster.js +64 -0
  352. package/dist/toolsFlagNames.d.ts +8 -0
  353. package/dist/toolsFlagNames.js +110 -0
  354. package/dist/turnFailurePresentation.js +41 -96
  355. package/dist/usageLimitAutoResume.d.ts +74 -0
  356. package/dist/usageLimitAutoResume.js +227 -0
  357. package/dist/userAttachmentEnvelope.js +1 -1
  358. package/dist/userAttachmentSeat.js +5 -5
  359. package/dist/userInvokedSkillsCapability.js +2 -2
  360. package/dist/webSearchBackendCapability.js +2 -2
  361. package/dist/wireErrorTriage.js +99 -50
  362. package/dist/wireFailureShape.d.ts +25 -1
  363. package/dist/wireFailureShape.js +92 -5
  364. package/dist/wireRefusalCopy.d.ts +75 -18
  365. package/dist/wireRefusalCopy.js +532 -148
  366. package/dist/wiringFleetDoctor.d.ts +3 -0
  367. package/dist/wiringFleetDoctor.js +21 -0
  368. package/dist/workflow.d.ts +2 -1
  369. package/dist/workflow.js +38 -1
  370. package/dist/workflowClient.d.ts +2 -7
  371. package/dist/workflowsJournalCapability.js +2 -2
  372. package/dist/writeProtectionCapability.js +2 -2
  373. package/docs/INTEGRATION-CLIENTS.md +1013 -190
  374. package/package.json +15 -4
@@ -1,10 +1,16 @@
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
4
  import { retiredAttachmentsKeyOf } from '../taskRequestWords.js';
4
5
  import { msg, renderMessage } from '../i18n/translate.js';
6
+ import { PROJECT_MEMORY_DISABLED } from '../projectMemory.js';
7
+ import { frameFamiliesJudgementOf } from '../approvalFrameFamilies.js';
5
8
  import { outputStyleSeatAdmitted } from '../outputStyleSeatCapability.js';
6
9
  import { refuseBadApprovalReach } from '../approvalReachWire.js';
7
- import { HOST_SUPPLIED_KEYS, HostSettingsRefused, buildHostTurnSettings, hostTurnSettingsOf, rethrowHostResolvedDeferred, } from '../hostTurnSettings.js';
10
+ import { HOST_SUPPLIED_KEYS, UNWIRED_HOST_TURN_SETTINGS, buildHostTurnSettings, hostIntakeSnapshotOf, hostFileHistoryRestoreNotice, hostProjectionNotice, hostTurnSettingsOf, intersectHostHooks, rethrowHostResolvedDeferred, hostSettingsRefusedError, } from '../hostTurnSettings.js';
11
+ import { SUBMISSION_ORIGINS, SUBMISSION_SURFACES, SUBMISSION_TOOLS, submissionAxesOf, } from './submissionIdentity.js';
12
+ import { withAssemblyRefusal } from '../wireFailureShape.js';
13
+ import { isPlainRecord, materializeJsonFaithful, shapeTag } from '../jsonFaithful.js';
8
14
  export const REQUEST_FIELD_MATRIX = [
9
15
  { field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
10
16
  { field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
@@ -18,15 +24,17 @@ export const REQUEST_FIELD_MATRIX = [
18
24
  { field: 'enableFork', lanes: ['interactive', 'print'], live: true, why: '显式布尔才 stamp([503]② fail-OPEN 教训:off 必须显式)', whyUtilityLane: '无座:分叉是会话面的动作,而 side-channel 提交是一次性的独立会话' },
19
25
  { field: 'attachments', lanes: ['interactive', 'print'], live: true, why: 'design/133 + G1 缺省开的 turn 边界 attachment', whyUtilityLane: '无座:turn 边界的附件配置属用户会话;side-channel 提交不带附件' },
20
26
  { 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 一行同理)' },
27
+ { 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
28
  { field: 'mcpServers', lanes: ['interactive', 'print'], live: true, why: '本地 .mcp.json 投影;service 单用户门决定是否兑现', whyUtilityLane: '无座:MCP 服务挂的是工具,与零工具面矛盾' },
23
29
  { field: 'settings.webSearch', lanes: ['interactive', 'print'], live: true, why: 'SEMA_WEBSEARCH_* / settings.json;per-request 配置赢过部署 env', whyUtilityLane: '无座:联网检索是一只工具,与零工具面矛盾' },
24
30
  { field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire', whyUtilityLane: '无座:hook 挂在工具门上(零工具面没有门),而且 side-channel 是壳自己的提交,不该继承用户那份 hook 表' },
31
+ { 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
32
  { 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
33
  { field: 'repo', lanes: ['interactive', 'print'], live: true, why: 'scan/code-review 场景的仓库坐标(passthrough;server 是权威校验方,本层不铸第二判官)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
27
34
  { field: 'memoryCapture', lanes: ['interactive', 'print', 'utility'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryWrite:false 不是一个轴(那是「本 run 只读平面」)。⚠️ parked 续跑腿结构性拿不到声明(决策/续跑端点从持久 body 重建 spec),那是引擎侧事实,不是本层的漏', whyUtilityLane: '有座:side-channel 提交是壳自己的一次性问答,**绝不该**进用户的长期记忆 —— 这一位正是那句声明,且它是收紧方向,带上去只会更严' },
28
35
  { 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' },
36
+ { 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' },
37
+ { 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
38
  { 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
39
  { 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
40
  { field: 'disableSkills', lanes: ['interactive', 'print'], live: true, why: '「这一发一只技能都不装」的**请求级声明**(出厂技能、调用方给的技能、子代定义里的技能一个都不进引擎;其余工具照挂 —— 与 excludeAllTools 是两件事,两键同带各自生效)。两条用户车道都是提交面 ⇒ 都能声明。本层只认字面 true(false 与缺席同义、不发);非布尔坏值响亮拒(上游对它 400);与非空 skills 或任一非空 agents[].skills 同在 ⇒ 装配期按名响亮拒(上游对这一组合 400),兜底层落 agents 之后同判。🔴 只在能力位读数为真时发(老服务端对表外键整条 400),值由片段口 disableSkillsDeclaration 唯一铸出', whyUtilityLane: '无座:同 skills 一行 —— side-channel 是零工具面,装不装技能都无从谈起' },
@@ -38,7 +46,7 @@ export const REQUEST_FIELD_MATRIX = [
38
46
  { field: 'outputRetries', lanes: ['print', 'utility'], live: true, why: '结构化输出的重试轮数(sdk `TaskRequest.outputRetries`;与 `outputSchema` 是**成对旋钮** —— 没有 schema 时引擎忽略它)。🔴 座位集与 `outputSchema` 那一行**逐字相同**:一半有座一半没座 = 端调得动重试轮数却调不动它所服务的约束(同座孪生分开定座,正是「主轴改了、孪生漏了」那一族)。上游接受域很窄(有限、≥1、向下取整、封顶 10),**表外一律整键省略**并回落引擎缺省 ⇒ 本层对坏值**响亮拒**(见 `refuseBadOutputRetries`),但**不预夹** [1,10]:夹是上游的事,本层不铸第二判官', whyUtilityLane: '有座:与 outputSchema 同座孪生 —— side-channel 要机读小答案时,重试轮数是同一个旋钮的另一半' },
39
47
  { field: 'maxCostUsd', lanes: ['print', 'utility'], live: true, why: '花费上限声明(sdk `TaskRequest.maxCostUsd`,请求体**顶层**位;上游向下夹到部署上限 —— 只许要得更少)。与 `limits` 同一条理由只在无人值守车道:交互面由人随时中断。🔴 坏值(非数 / 非有限 / 非正)在本层**响亮拒**:上游对这一位的坏值是安静忽略并回落部署上限,不是 4xx(见 `refuseBadMaxCostUsd`)', whyUtilityLane: '有座:小预算是本车道的定义之一(与 limits 同一条理由);上游向下夹到部署上限,只许要得更少' },
40
48
  { 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 没有上传面' },
49
+ { 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
50
  { 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
51
  { field: 'allowTools', lanes: ['interactive', 'print'], live: true, why: '引擎的可用性选择器(收紧方向的声明:只给点名的工具;`[]` = 什么都不给,照发)。两条用户车道都是提交面、都有「只给这几只工具」的用户入口(终端 `--tools`)⇒ 都有座;坏形(不是串数组)在装配期响亮拒,不静默删键', whyUtilityLane: '无座:side-channel 恒带 excludeAllTools(零工具面),可用性选择器在零工具面上没有可选的东西' },
44
52
  { 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 +54,8 @@ export const REQUEST_FIELD_MATRIX = [
46
54
  { 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
55
  { 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
56
  { 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 是壳自己的一次性提交,没有用户 `@` 附件' },
57
+ { field: 'images', lanes: ['interactive', 'print'], live: false, why: '图片输入(sdk `TaskRequest.images`;base64 / url 两形,原样透传)。两条用户车道都是提交面、都有来源:交互面 = 粘贴图与 `@` 图片像素,一次性车道 = 结构化输入里的图片块。张数、单张与合计的座位判定归人类提交装配口 `encodeHumanTurn`(两条车道同一份判定,本表不另判);请求体与图片张数的上限归上游判', whyUtilityLane: '无座:贴图是交互动作,side-channel 的提交体里没有图片块', origins: ['user'], whyOriginLimited: '只给用户所写的提交:图片是用户本人贴进 / 点名进这一条消息的内容;注入件(计划复核结局等)与客户端自发的旁路提交没有用户贴的图片,带上去就是把别处的图片当成这一发的输入' },
58
+ { field: 'userAttachments', lanes: ['interactive', 'print'], live: false, why: '请求层附件(sdk `TaskRequest.userAttachments`:挂在这次提交目标上的 `@` 文本类附件条目,引擎铸帧、在第一次模型调用之前判形)。两条用户车道都是提交面、都经同一只装配口 `encodeHumanTurn`(条目只在这台引擎的席位读数判 `yes` 时出;老服务端对这一键 400,所以没有条目就不发);空数组 = 没带附件 ⇒ 整键不 stamp。座位集与 `images` 一行逐字相同', whyUtilityLane: '无座:side-channel 是壳自己的一次性提交,没有用户 `@` 附件', origins: ['user'], whyOriginLimited: '只给用户所写的提交:请求层附件是用户本人 `@` 进这一条消息的附件;注入件与客户端自发的旁路提交不带用户的附件' },
51
59
  { 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
60
  { field: 'clientContext', lanes: ['interactive'], live: false, why: 'IANA 时区 + 可选邮箱 → core 本地化 `# Environment` 的 today;print 同样跑在用户机器上,缺席让引擎误标 (UTC)', gap: true, whyUtilityLane: '无座:本地化 today 的用途在用户会话的环境段,而 side-channel 的系统提示由壳整段给出 —— 这一列我判不准要不要(与本行 `gap` 同一条待办:要补先单独立项)' },
53
61
  { field: 'scratchpadDir', lanes: ['interactive'], live: true, why: '[816]③ per-session 暂存目录;print 也有 session,缺席让 `-p` 的工具写不进 exemptDir', gap: true, whyUtilityLane: '无座:per-session 暂存目录是给**工具**写的,零工具面上没人写' },
@@ -70,12 +78,14 @@ export const TASK_REQUEST_OMISSION_CAUSES = Object.freeze([
70
78
  'upstream_absent',
71
79
  'other_channel',
72
80
  'off_lane',
81
+ 'off_origin',
73
82
  'not_live',
74
83
  ]);
75
84
  const TASK_REQUEST_OMISSION_CAUSE_SENTENCES = Object.freeze({
76
85
  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
86
  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
87
  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',
88
+ 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
89
  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
90
  });
81
91
  export function taskRequestOmissionCauseDetail(cause) {
@@ -98,6 +108,10 @@ const TASK_REQUEST_OMISSION_CAUSE_NOTICE_PARTS = Object.freeze({
98
108
  lead: 'Not sent with this request',
99
109
  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
110
  },
111
+ off_origin: {
112
+ lead: 'Not sent with this request',
113
+ 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',
114
+ },
101
115
  not_live: {
102
116
  lead: 'Not sent with this request',
103
117
  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',
@@ -168,18 +182,29 @@ const INPUT_KEY_ROUTES = {
168
182
  why: '思考 token 数上限:sdk `TaskRequest` 上思考强度只有档位词(`reasoningEffort` 七词闭集),没有 token 数这一位;把数字折成档位词 = 替用户编一个他没说过的档位',
169
183
  },
170
184
  memoryCapture: row('memoryCapture'),
171
- memory: row('memory'),
172
- fileHistory: row('fileHistory'),
173
185
  inheritEnv: row('inheritEnv'),
186
+ projectMemory: row('projectMemory'),
174
187
  requireExistingSession: row('requireExistingSession'),
175
188
  disableSkills: row('disableSkills'),
176
189
  rewind: row('resumeAt/resumeAtMode/restoreFiles/rewindFilesTo'),
177
190
  repo: row('repo'),
178
191
  allowTools: row('allowTools'),
179
192
  hostSettings: { via: 'control' },
193
+ approvalFrameFamiliesReading: { via: 'control' },
180
194
  settings: { via: 'settings' },
181
195
  };
196
+ const HOST_TOP_KEY_ROUTES = {
197
+ memory: row('memory'),
198
+ fileHistory: row('fileHistory'),
199
+ };
200
+ const HOST_TOP_KEYS = Object.freeze(Object.keys(HOST_TOP_KEY_ROUTES));
201
+ const _hostTopKeysPin = [true];
202
+ void _hostTopKeysPin;
203
+ const _projectMemoryWordPin = [true, true];
204
+ void _projectMemoryWordPin;
205
+ const HOST_TOP_ASK_AFTER = 'memoryCapture';
182
206
  const HOST_SETTINGS_SUBKEYS = Object.freeze(HOST_SUPPLIED_KEYS.filter((k) => k.startsWith('settings.')).map((k) => k.slice('settings.'.length)));
207
+ const MINTED_SETTINGS_SUBKEYS = Object.freeze(['approvalFrameFamilies']);
183
208
  const _hostSubkeysPin = [true, true, true, true];
184
209
  void _hostSubkeysPin;
185
210
  const SETTINGS_KEY_ROUTES = {
@@ -187,6 +212,7 @@ const SETTINGS_KEY_ROUTES = {
187
212
  webSearch: row('settings.webSearch'),
188
213
  ultracode: row('settings.ultracode'),
189
214
  hooks: row('settings.hooks'),
215
+ approvalFrameFamilies: row('settings.approvalFrameFamilies'),
190
216
  outputStyle: { via: 'always' },
191
217
  };
192
218
  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 })] : []));
@@ -195,6 +221,8 @@ const liveDefaultKeyNames = () => new Set(LIVE_DEFAULT_FIELDS.flatMap(item => it
195
221
  const routeOfTopLevelKey = (key) => {
196
222
  if (hasOwn(INPUT_KEY_ROUTES, key))
197
223
  return INPUT_KEY_ROUTES[key];
224
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key))
225
+ return HOST_TOP_KEY_ROUTES[key];
198
226
  if (!key.includes('.') && liveDefaultKeyNames().has(key))
199
227
  return { via: 'fixed', cause: 'other_channel' };
200
228
  return undefined;
@@ -218,18 +246,20 @@ const OMISSION_JUDGES = {
218
246
  upstream_absent: s => s.via === 'fixed' && s.cause === 'upstream_absent',
219
247
  other_channel: s => s.via === 'fixed' && s.cause === 'other_channel',
220
248
  off_lane: (s, lane) => s.via === 'matrix' && !s.spec.lanes.includes(lane),
249
+ off_origin: (s, _lane, _live, origin) => s.via === 'matrix' && origin !== undefined && s.spec.origins !== undefined && !s.spec.origins.includes(origin),
221
250
  not_live: (s, _lane, live) => s.via === 'matrix' && s.spec.live && !live,
222
251
  };
223
- const omissionCauseOf = (s, lane, live) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live));
224
- const hostRefusalNoticeOf = (host, lane, live, verdictNotice) => {
252
+ const omissionCauseOf = (s, lane, live, origin) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live, origin));
253
+ const hostRefusalNoticeOf = (intake, lane, live, origin) => {
254
+ const verdictNotice = intake.notice ?? hostProjectionNotice();
225
255
  try {
226
- const refusing = host.rows.filter((row) => row.disposition === 'refuse_turn');
256
+ const refusing = intake.refusingKeys;
227
257
  if (refusing.length === 0)
228
258
  return verdictNotice;
229
- const turnNotices = host.notices.filter((n) => n.plane === 'turn');
230
- const aligned = turnNotices.length === refusing.length;
231
- for (const [i, row] of refusing.entries()) {
232
- if (!hostRefuseRowSeated(row.key, lane, live))
259
+ const turnNotices = intake.turnNotices;
260
+ const aligned = turnNotices !== undefined && turnNotices.length === refusing.length;
261
+ for (const [i, key] of refusing.entries()) {
262
+ if (!hostRefuseRowSeated(key, lane, live, origin))
233
263
  continue;
234
264
  return aligned ? (turnNotices[i] ?? verdictNotice) : verdictNotice;
235
265
  }
@@ -239,7 +269,13 @@ const hostRefusalNoticeOf = (host, lane, live, verdictNotice) => {
239
269
  return verdictNotice;
240
270
  }
241
271
  };
242
- const hostRefuseRowSeated = (key, lane, live) => {
272
+ const hostRefuseRowSeated = (key, lane, live, origin) => {
273
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key)) {
274
+ const top = HOST_TOP_KEY_ROUTES[key];
275
+ return top === undefined || (top.via !== 'matrix' && top.via !== 'fixed') || omissionCauseOf(standingOf(top), lane, live, origin) === undefined;
276
+ }
277
+ if (!key.startsWith('settings.'))
278
+ return true;
243
279
  const sub = key.slice('settings.'.length);
244
280
  if (!hasOwn(SETTINGS_KEY_ROUTES, sub))
245
281
  return true;
@@ -247,7 +283,7 @@ const hostRefuseRowSeated = (key, lane, live) => {
247
283
  if (route === undefined || route.via === 'always')
248
284
  return true;
249
285
  if (route.via === 'matrix' || route.via === 'fixed')
250
- return omissionCauseOf(standingOf(route), lane, live) === undefined;
286
+ return omissionCauseOf(standingOf(route), lane, live, origin) === undefined;
251
287
  return true;
252
288
  };
253
289
  const REQUEST_LANE_SET = { interactive: true, print: true, utility: true };
@@ -258,6 +294,60 @@ const refuseBadLane = (lane) => {
258
294
  throw new TypeError(`buildTaskRequest: 车道实参只接受 ${Object.keys(REQUEST_LANE_SET).map(l => `'${l}'`).join(' / ')} —— 收到 ${coarseShape(lane)}。` +
259
295
  '表外的车道词会让车道表里每一行都判「不属于本车道」,请求体只剩 objective 与 sessionId 而没有任何信号,所以在装配前拒。错误文本只报形状,不回显值。');
260
296
  };
297
+ const laneAxesOf = (arg, port) => {
298
+ if (typeof arg !== 'object' || arg === null) {
299
+ refuseBadLane(arg);
300
+ return { lane: arg, origin: undefined };
301
+ }
302
+ const axes = submissionAxesOf(arg);
303
+ if (axes !== undefined)
304
+ return axes;
305
+ let shape;
306
+ try {
307
+ shape = coarseShape(arg);
308
+ }
309
+ catch {
310
+ shape = 'typeof=object / unreadable';
311
+ }
312
+ const err = new TypeError(renderMessage(msg('config.taskRequest.submissionIdentityMalformed', {
313
+ shape,
314
+ lanes: REQUEST_LANES.join(' / '),
315
+ surfaces: SUBMISSION_SURFACES.join(' / '),
316
+ origins: SUBMISSION_ORIGINS.join(' / '),
317
+ tools: SUBMISSION_TOOLS.join(' / '),
318
+ })));
319
+ throw port === 'assembly' ? withAssemblyRefusal(err, 'input_malformed') : err;
320
+ };
321
+ const originAxisFaultOf = (origins, why, lanes) => {
322
+ if (origins === undefined)
323
+ return '写了 whyOriginLimited 却没写 origins(理由没有对象)';
324
+ if (typeof why !== 'string' || why.length === 0)
325
+ return '写了 origins 却没写 whyOriginLimited(来源受限必须带理由)';
326
+ if (!Array.isArray(origins) || origins.length === 0)
327
+ return 'origins 不是非空数组';
328
+ if (!origins.every((o) => SUBMISSION_ORIGINS.includes(o)) || new Set(origins).size !== origins.length) {
329
+ return 'origins 里有出身闭集外的词或重复词';
330
+ }
331
+ if (origins.length === SUBMISSION_ORIGINS.length)
332
+ return 'origins 是全集(与缺省同义:两种写法说同一件事,删掉这一位)';
333
+ if (!origins.includes('user'))
334
+ return 'origins 不含 user(成因 off_origin 的终端用户句说「这一发被标成不是人写的」,对这一行会是假话;真有这种行时先加成因词与句子)';
335
+ if (!origins.includes('shell') && lanes.includes('utility'))
336
+ return 'origins 排除壳出身却占 utility 座(身份形派生到 utility 必是壳出身,那个座永远到不了)';
337
+ return undefined;
338
+ };
339
+ const refuseOriginAxisDrift = () => {
340
+ for (const r of REQUEST_FIELD_MATRIX) {
341
+ const origins = r.origins;
342
+ const why = r.whyOriginLimited;
343
+ if (origins === undefined && why === undefined)
344
+ continue;
345
+ const fault = originAxisFaultOf(origins, why, r.lanes);
346
+ if (fault === undefined)
347
+ continue;
348
+ throw new Error(`buildTaskRequest: 车道表 REQUEST_FIELD_MATRIX 的「${r.field}」一行来源轴不自洽 —— ${fault}。这是本包内部缺陷,不是调用方入参的问题。`);
349
+ }
350
+ };
261
351
  const present = (v) => v !== undefined && v !== null;
262
352
  const rewindSubKeys = () => {
263
353
  const r = INPUT_KEY_ROUTES.rewind;
@@ -275,6 +365,7 @@ const VALUE_LEVEL_IN_PLAY = Object.freeze({
275
365
  memoryCapture: (v) => v === 'off',
276
366
  memory: (v) => v === 'off',
277
367
  fileHistory: (v) => v === FILE_HISTORY_DISABLED,
368
+ projectMemory: (v) => v === PROJECT_MEMORY_DISABLED,
278
369
  requireExistingSession: (v) => v === true,
279
370
  disableSkills: (v) => v === true,
280
371
  rewind: (v) => typeof v === 'object' && v !== null && rewindSubKeys().some((k) => hasOwn(v, k) && present(v[k])),
@@ -289,15 +380,16 @@ const snapshotByName = (source, keys, seed = {}) => {
289
380
  const UNKNOWN_KEY_DISPLAY_MAX = 64;
290
381
  const MAX_UNKNOWN_KEYS_SHOWN = 16;
291
382
  const keyForDisplay = (k) => k.length > UNKNOWN_KEY_DISPLAY_MAX ? `${JSON.stringify(k.slice(0, UNKNOWN_KEY_DISPLAY_MAX))}(已截短)` : JSON.stringify(k);
292
- export function taskRequestSeatOf(key, lane) {
293
- refuseBadLane(lane);
383
+ export function taskRequestSeatOf(key, laneOrIdentity) {
384
+ const { lane, origin } = laneAxesOf(laneOrIdentity, 'seat');
385
+ refuseOriginAxisDrift();
294
386
  const route = typeof key === 'string' ? routeOfInputKey(key) : undefined;
295
387
  if (route === undefined)
296
388
  return SEAT_UNKNOWN;
297
389
  if (route.via === 'always' || route.via === 'control' || route.via === 'settings')
298
390
  return SEATED_UNGATED;
299
391
  const standing = standingOf(route);
300
- const cause = omissionCauseOf(standing, lane, true);
392
+ const cause = omissionCauseOf(standing, lane, true, origin);
301
393
  if (cause !== undefined && cause !== 'not_live')
302
394
  return Object.freeze({ seat: 'none', cause });
303
395
  return standing.via === 'matrix' && standing.spec.live ? SEATED_LIVE_GATED : SEATED_UNGATED;
@@ -309,99 +401,6 @@ const foreignSettingsSubKeys = (lane) => REQUEST_FIELD_MATRIX.filter(f => f.fiel
309
401
  const namedSettingsSubKeys = () => REQUEST_FIELD_MATRIX.filter(f => f.field.startsWith('settings.') && f.field !== 'settings.<resolved>').map(f => f.field.slice('settings.'.length));
310
402
  const PERMISSION_FACE_KEY = 'permissions';
311
403
  const coarseShape = (v) => v === null ? 'typeof=object / null' : Array.isArray(v) ? 'typeof=object / array' : `typeof=${typeof v}`;
312
- const shapeTag = (v) => {
313
- try {
314
- const proto = Object.getPrototypeOf(v);
315
- const carrier = proto === null
316
- ? 'null-prototype'
317
- : String(proto?.constructor?.name ?? 'anonymous');
318
- return `typeof=${typeof v} / ${Object.prototype.toString.call(v)} / prototype=${carrier}`;
319
- }
320
- catch {
321
- return `typeof=${typeof v} / shape=unreadable`;
322
- }
323
- };
324
- const isPlainRecord = (v) => {
325
- if (typeof v !== 'object' || v === null)
326
- return false;
327
- const proto = Object.getPrototypeOf(v);
328
- if (proto === null)
329
- return true;
330
- if (typeof proto !== 'object')
331
- return false;
332
- if (!Object.prototype.hasOwnProperty.call(proto, 'constructor'))
333
- return false;
334
- const ctor = proto.constructor;
335
- if (typeof ctor !== 'function')
336
- return false;
337
- if (Function.prototype.toString.call(ctor) !== Function.prototype.toString.call(Object))
338
- return false;
339
- return ctor.prototype === proto;
340
- };
341
- const materializeJsonFaithful = (v, path, seen = new Set()) => {
342
- if (v === null)
343
- return { value: null };
344
- const t = typeof v;
345
- if (t === 'string' || t === 'boolean')
346
- return { value: v };
347
- if (t === 'number') {
348
- return Number.isFinite(v) ? { value: v } : { bad: `${path}(非有限数 ⇒ 序列化成 null)` };
349
- }
350
- if (t !== 'object')
351
- return { bad: `${path}(typeof=${t} 不是 JSON 值)` };
352
- const o = v;
353
- if (seen.has(o))
354
- return { bad: `${path}(循环引用)` };
355
- const toJSONProp = o.toJSON;
356
- if (typeof toJSONProp === 'function')
357
- return { bad: `${path}(自报 toJSON ⇒ 字节被改写)` };
358
- seen.add(o);
359
- try {
360
- if (Array.isArray(o)) {
361
- const rawLen = o.length;
362
- if (typeof rawLen === 'bigint' || typeof rawLen === 'symbol') {
363
- return { bad: `${path}(length 是 ${typeof rawLen} ⇒ 原生序列化直接抛,归 0 会静默发空表)` };
364
- }
365
- let lenNum;
366
- try {
367
- lenNum = +rawLen;
368
- }
369
- catch {
370
- return { bad: `${path}(length 按 ToNumber 转换即抛(BigInt/symbol 形)⇒ 原生序列化也抛,归 0 会静默发空表)` };
371
- }
372
- if (lenNum === Number.POSITIVE_INFINITY) {
373
- return { bad: `${path}(length 是 +Infinity ⇒ 原生序列化抛 RangeError,归 0 会静默发空表)` };
374
- }
375
- const len = Number.isNaN(lenNum)
376
- ? 0
377
- : Math.min(Math.max(0, Math.trunc(lenNum)), Number.MAX_SAFE_INTEGER);
378
- const items = [];
379
- for (let i = 0; i < len; i++) {
380
- const r = materializeJsonFaithful(o[i], `${path}[${i}]`, seen);
381
- if ('bad' in r)
382
- return r;
383
- items.push(r.value);
384
- }
385
- return { value: items };
386
- }
387
- const rec = Object.create(null);
388
- for (const k of Object.keys(o)) {
389
- const val = o[k];
390
- if (val === undefined)
391
- continue;
392
- const r = materializeJsonFaithful(val, `${path}.${k}`, seen);
393
- if ('bad' in r)
394
- return r;
395
- Object.defineProperty(rec, k, { value: r.value, enumerable: true, writable: true, configurable: true });
396
- }
397
- if (!isPlainRecord(o))
398
- return { bad: `${path}(${shapeTag(o)})` };
399
- return { value: rec };
400
- }
401
- finally {
402
- seen.delete(o);
403
- }
404
- };
405
404
  const resolvedSnapshotForWire = (resolved) => {
406
405
  if (resolved === undefined || resolved === null)
407
406
  return {};
@@ -444,16 +443,106 @@ const resolvedSnapshotForWire = (resolved) => {
444
443
  return out;
445
444
  };
446
445
  export function hostTurnSettings(input) {
447
- return buildHostTurnSettings(input, (snapshot) => resolvedSnapshotForWire(snapshot));
446
+ return buildHostTurnSettings(input, { materialize: (snapshot) => resolvedSnapshotForWire(snapshot), memoryOffDeclared });
447
+ }
448
+ const fileRestoreRequested = (restoreFiles, rewindFilesTo, rewindFiles) => restoreFiles === true || (typeof rewindFilesTo === 'string' && rewindFilesTo.length > 0) || rewindFiles === true;
449
+ function tightenedRequestOf(request, intake, lane) {
450
+ const readData = (key) => {
451
+ try {
452
+ if (!hasOwn(request, key))
453
+ return { ok: true, value: undefined };
454
+ const d = Object.getOwnPropertyDescriptor(request, key);
455
+ if (d === undefined)
456
+ return { ok: true, value: undefined };
457
+ if (!Object.hasOwn(d, 'value'))
458
+ return { ok: false };
459
+ if (d.enumerable !== true)
460
+ return { ok: true, value: undefined };
461
+ const got = request[key];
462
+ return got === d.value ? { ok: true, value: d.value } : { ok: false };
463
+ }
464
+ catch {
465
+ return { ok: false };
466
+ }
467
+ };
468
+ const unjudged = { kind: 'refuse', notice: hostProjectionNotice() };
469
+ const add = {};
470
+ const tightened = [];
471
+ if (intake.carried.memory === MEMORY_OFF && hostRefuseRowSeated('memory', lane, true)) {
472
+ const sent = readData('memory');
473
+ if (!sent.ok)
474
+ return unjudged;
475
+ if (sent.value !== MEMORY_OFF) {
476
+ add.memory = MEMORY_OFF;
477
+ appendOwnElement(tightened, 'memory');
478
+ }
479
+ }
480
+ if (intake.carried.fileHistory === FILE_HISTORY_DISABLED && hostRefuseRowSeated('fileHistory', lane, true)) {
481
+ const sent = readData('fileHistory');
482
+ if (!sent.ok)
483
+ return unjudged;
484
+ if (sent.value !== FILE_HISTORY_DISABLED) {
485
+ const restoreFiles = readData('restoreFiles');
486
+ const rewindFilesTo = readData('rewindFilesTo');
487
+ const rewindFiles = readData('rewindFiles');
488
+ if (!restoreFiles.ok || !rewindFilesTo.ok || !rewindFiles.ok)
489
+ return unjudged;
490
+ if (fileRestoreRequested(restoreFiles.value, rewindFilesTo.value, rewindFiles.value))
491
+ return { kind: 'refuse', notice: hostFileHistoryRestoreNotice() };
492
+ add.fileHistory = FILE_HISTORY_DISABLED;
493
+ appendOwnElement(tightened, 'fileHistory');
494
+ }
495
+ }
496
+ if (tightened.length === 0)
497
+ return { kind: 'ok', request, tightened: Object.freeze([]) };
498
+ return { kind: 'ok', request: { ...request, ...add }, tightened: Object.freeze(tightened) };
499
+ }
500
+ export function refilterHostSettings(request, fresh, lane) {
501
+ refuseBadLane(lane);
502
+ const host = hostTurnSettingsOf(fresh);
503
+ const intake = host === undefined || host === UNWIRED_HOST_TURN_SETTINGS ? undefined : hostIntakeSnapshotOf(host);
504
+ if (host === undefined || intake === undefined) {
505
+ throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(fresh) })));
506
+ }
507
+ const refuseNotice = () => intake.notice ?? hostProjectionNotice();
508
+ if (intake.refuses) {
509
+ const refusing = hostRefusalNoticeOf(intake, lane, true, undefined);
510
+ if (refusing !== undefined)
511
+ return Object.freeze({ kind: 'refuse', notice: refusing });
512
+ }
513
+ const tight = tightenedRequestOf(request, intake, lane);
514
+ if (tight.kind === 'refuse')
515
+ return Object.freeze({ kind: 'refuse', notice: tight.notice });
516
+ const base = tight.request;
517
+ const tightened = tight.tightened;
518
+ const settings = base.settings;
519
+ if (!isPlainRecord(settings) || !Object.hasOwn(settings, 'hooks'))
520
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
521
+ const disposition = intake.dispositions['settings.hooks'];
522
+ if (disposition === 'unwired')
523
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
524
+ const now = intake.carried.hooks;
525
+ const sentHooks = settings.hooks;
526
+ if (!isPlainRecord(sentHooks))
527
+ return Object.freeze({ kind: 'refuse', notice: refuseNotice() });
528
+ const cut = intersectHostHooks(sentHooks, now);
529
+ if (!cut.judged)
530
+ return Object.freeze({ kind: 'refuse', notice: refuseNotice() });
531
+ if (cut.dropped === 0)
532
+ return Object.freeze({ kind: 'send', request: base, hooksDropped: 0, tightened });
533
+ const { hooks: _sentHooks, ...restSettings } = settings;
534
+ void _sentHooks;
535
+ const nextSettings = cut.hooks === undefined ? restSettings : { ...restSettings, hooks: cut.hooks };
536
+ const { settings: _sentSettings, ...restRequest } = base;
537
+ void _sentSettings;
538
+ const next = Object.keys(nextSettings).length > 0 ? { ...restRequest, settings: nextSettings } : restRequest;
539
+ return Object.freeze({ kind: 'send', request: next, hooksDropped: cut.dropped, tightened });
448
540
  }
449
541
  export const MEMORY_CAPTURE_OFF = 'off';
450
542
  export function memoryCaptureDeclarationField(declared) {
451
543
  return declared ? { memoryCapture: MEMORY_CAPTURE_OFF } : {};
452
544
  }
453
545
  export const MEMORY_OFF = 'off';
454
- export function memoryOffDeclarationField(declared) {
455
- return declared ? { memory: MEMORY_OFF } : {};
456
- }
457
546
  export const FILE_HISTORY_DISABLED = 'disabled';
458
547
  export const INHERIT_ENV_WORDS = Object.freeze(['all', 'scrub']);
459
548
  export function isInheritEnvWord(v) {
@@ -481,16 +570,6 @@ export function memoryOffDeclared(input) {
481
570
  }
482
571
  return flag === MEMORY_OFF || fromSettings;
483
572
  }
484
- const refuseBadMemoryOff = (v) => {
485
- if (v === undefined || v === null)
486
- return;
487
- if (v === 'off')
488
- return;
489
- throw new TypeError('buildTaskRequest: memory 只接受字面量 `\'off\'` 或缺席(undefined/null)—— ' +
490
- `收到 ${coarseShape(v)}。这一位是**请求级整面开关**(「这一次别给我记忆」),上游是**单成员闭集**(没有 ` +
491
- '`"on"`,`{ enabled: false }` 对象形也不是 wire 拼法)且对坏拼写**响亮拒**(server 400 `request.field_invalid`)。' +
492
- '本层若静默删键,这一次运行会照常挂上记忆面而声明人不会知道 —— 与 memoryCapture 同一条纪律。错误文本只报形状,不回显值。');
493
- };
494
573
  const withoutRetiredAttachmentsKeys = (v) => {
495
574
  if (typeof v !== 'object' || v === null)
496
575
  return v;
@@ -502,7 +581,7 @@ const withoutRetiredAttachmentsKeys = (v) => {
502
581
  const d = Object.getOwnPropertyDescriptor(v, key);
503
582
  if (d !== undefined && Object.prototype.hasOwnProperty.call(d, 'value') && d.value === undefined) {
504
583
  ;
505
- (drop ??= []).push(key);
584
+ appendOwnElement((drop ??= []), key);
506
585
  continue;
507
586
  }
508
587
  throw new AttachmentsSpecRefused(`buildTaskRequest: refusing attachments key ${JSON.stringify(key)} — ${why}`, { token: key, why, allowed: ATTACHMENTS_SPEC_KEYS, key });
@@ -521,7 +600,7 @@ function allowToolsSnapshot(v) {
521
600
  return v;
522
601
  let n;
523
602
  try {
524
- n = Object.getOwnPropertyDescriptor(v, 'length')?.value;
603
+ n = readOwnDataValue(v, 'length');
525
604
  }
526
605
  catch {
527
606
  return ALLOW_TOOLS_UNREADABLE_ITEM;
@@ -531,27 +610,31 @@ function allowToolsSnapshot(v) {
531
610
  const out = new Array(n);
532
611
  for (let k = 0; k < n; k += 1) {
533
612
  let d;
613
+ let unreadable = false;
534
614
  try {
535
615
  d = Object.getOwnPropertyDescriptor(v, k);
536
616
  }
537
617
  catch {
538
- out[k] = ALLOW_TOOLS_UNREADABLE_ITEM;
539
- continue;
618
+ unreadable = true;
540
619
  }
541
- if (d === undefined)
542
- continue;
543
- out[k] = 'value' in d ? d.value : ALLOW_TOOLS_UNREADABLE_ITEM;
620
+ if (unreadable)
621
+ defineOwnIndex(out, k, ALLOW_TOOLS_UNREADABLE_ITEM);
622
+ else if (d !== undefined)
623
+ defineOwnIndex(out, k, Object.hasOwn(d, 'value') ? d.value : ALLOW_TOOLS_UNREADABLE_ITEM);
544
624
  }
545
625
  return Object.freeze(out);
546
626
  }
627
+ const defineOwnIndex = (list, k, value) => {
628
+ Object.defineProperty(list, k, { value, writable: true, enumerable: true, configurable: true });
629
+ };
547
630
  const allowToolsCopy = (v) => {
548
631
  if (!Array.isArray(v))
549
632
  return [];
550
633
  const out = [];
551
634
  for (let k = 0; k < v.length; k += 1) {
552
635
  const d = Object.getOwnPropertyDescriptor(v, k);
553
- if (d !== undefined && 'value' in d && typeof d.value === 'string')
554
- out.push(d.value);
636
+ if (d !== undefined && Object.hasOwn(d, 'value') && typeof d.value === 'string')
637
+ appendOwnElement(out, d.value);
555
638
  }
556
639
  return out;
557
640
  };
@@ -562,7 +645,7 @@ const refuseBadAllowTools = (v) => {
562
645
  let ok = true;
563
646
  for (let k = 0; k < v.length; k += 1) {
564
647
  const d = Object.getOwnPropertyDescriptor(v, k);
565
- if (d === undefined || !('value' in d) || typeof d.value !== 'string') {
648
+ if (d === undefined || !Object.hasOwn(d, 'value') || typeof d.value !== 'string') {
566
649
  ok = false;
567
650
  break;
568
651
  }
@@ -581,7 +664,7 @@ const refuseBadUserInvokedSkills = (v) => {
581
664
  let ok = true;
582
665
  for (let k = 0; k < v.length; k += 1) {
583
666
  const d = Object.getOwnPropertyDescriptor(v, k);
584
- if (d === undefined || !('value' in d) || typeof d.value !== 'string') {
667
+ if (d === undefined || !Object.hasOwn(d, 'value') || typeof d.value !== 'string') {
585
668
  ok = false;
586
669
  break;
587
670
  }
@@ -598,15 +681,6 @@ const refuseBadExcludeAllTools = (v) => {
598
681
  `收到 ${coarseShape(v)}。这一位是**整只工具面卸载**的收紧声明:本层若把坏值静默删键,这一次运行会挂上声明人以为已经卸掉的全部工具。` +
599
682
  '错误文本只报形状,不回显值。');
600
683
  };
601
- const refuseBadFileHistory = (v) => {
602
- if (v === undefined || v === null)
603
- return;
604
- if (v === FILE_HISTORY_DISABLED)
605
- return;
606
- throw new TypeError('buildTaskRequest: fileHistory 只接受字面量 `\'disabled\'` 或缺席(undefined/null)—— ' +
607
- `收到 ${coarseShape(v)}。这一位是「这一次运行别捕获文件历史」的收紧声明,上游是**单成员闭集**(没有第二个词)且对坏值` +
608
- '**响亮拒**。本层若静默删键,这一次运行照常捕获而声明人不会知道。错误文本只报形状,不回显值。');
609
- };
610
684
  const refuseBadInheritEnv = (v) => {
611
685
  if (v === undefined || v === null)
612
686
  return;
@@ -616,6 +690,15 @@ const refuseBadInheritEnv = (v) => {
616
690
  '这一位是「模型驱动的宿主 shell 继承多少用户环境」的两词闭集声明,上游对词表外的值响亮拒;环境变量名单不是请求值。' +
617
691
  '本层若静默删键,这一次运行会按缺省清洗跑、还清掉本会话上一条声明,而声明人不会知道。错误文本只报形状,不回显值。');
618
692
  };
693
+ const refuseBadProjectMemory = (v) => {
694
+ if (v === undefined || v === null)
695
+ return;
696
+ if (v === PROJECT_MEMORY_DISABLED)
697
+ return;
698
+ throw new TypeError(`buildTaskRequest: projectMemory 只接受字面量 '${PROJECT_MEMORY_DISABLED}' 或缺席(undefined/null)—— 收到 ${coarseShape(v)}。` +
699
+ '这一位是「这一次运行别读部署的项目上下文」的收紧声明,引擎是**单成员闭集**(没有第二个词:要照常读就不带这一键)且对坏值拒启。' +
700
+ '本层若静默删键,请求会照发、项目指令文件会照常递给模型,而声明人不会知道自己的 opt-out 丢了。错误文本只报形状,不回显值。');
701
+ };
619
702
  const refuseBadRequireExistingSession = (v) => {
620
703
  if (v === undefined || v === null || v === true || v === false)
621
704
  return;
@@ -704,7 +787,7 @@ export function parseMaxCostUsd(text) {
704
787
  return { ok: false, why: 'not_positive' };
705
788
  return { ok: true, value };
706
789
  }
707
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'fileHistory', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills'];
790
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills', 'projectMemory'];
708
791
  const MATERIALIZE_AT_READ = Object.freeze({ allowTools: (v) => allowToolsSnapshot(v), userInvokedSkills: (v) => allowToolsSnapshot(v) });
709
792
  function dataFirstSnapshot(input, afterData) {
710
793
  let descs;
@@ -720,10 +803,10 @@ function dataFirstSnapshot(input, afterData) {
720
803
  const d = descs[k];
721
804
  if (d === undefined || (d.enumerable !== true && routeOfTopLevelKey(k) === undefined))
722
805
  continue;
723
- if ('value' in d)
806
+ if (Object.hasOwn(d, 'value'))
724
807
  bag[k] = Object.hasOwn(MATERIALIZE_AT_READ, k) ? MATERIALIZE_AT_READ[k](d.value) : d.value;
725
808
  else
726
- accessors.push(k);
809
+ appendOwnElement(accessors, k);
727
810
  }
728
811
  afterData?.(bag);
729
812
  const ordered = [
@@ -736,31 +819,35 @@ function dataFirstSnapshot(input, afterData) {
736
819
  }
737
820
  return bag;
738
821
  }
739
- export function assembleTaskRequest(input, lane) {
740
- refuseBadLane(lane);
822
+ export function assembleTaskRequest(input, laneOrIdentity) {
823
+ const { lane, origin } = laneAxesOf(laneOrIdentity, 'assembly');
824
+ refuseOriginAxisDrift();
741
825
  let resolvedInEarly;
742
826
  let resolvedMaterialized;
743
827
  let resolvedMaterializeError;
744
828
  let resolvedHookRan = false;
829
+ let intakeEarly;
830
+ const materializeAtRead = (product) => (raw) => {
831
+ resolvedInEarly = raw;
832
+ try {
833
+ rethrowHostResolvedDeferred(product);
834
+ resolvedMaterialized = resolvedSnapshotForWire(resolvedInEarly);
835
+ }
836
+ catch (e) {
837
+ resolvedMaterializeError = e;
838
+ }
839
+ };
745
840
  const src = dataFirstSnapshot(input, (bag) => {
746
841
  if (!Object.hasOwn(bag, 'hostSettings'))
747
842
  return;
748
843
  const product = hostTurnSettingsOf(bag['hostSettings'], 'shape');
749
844
  if (product !== undefined) {
750
845
  resolvedHookRan = true;
751
- resolvedInEarly = product.carried.resolved;
752
- try {
753
- rethrowHostResolvedDeferred(product);
754
- resolvedMaterialized = resolvedSnapshotForWire(resolvedInEarly);
755
- }
756
- catch (e) {
757
- resolvedMaterializeError = e;
758
- }
846
+ intakeEarly = hostIntakeSnapshotOf(product, materializeAtRead(product));
759
847
  }
760
848
  });
761
849
  const live = src.live;
762
850
  const memoryCaptureIn = src.memoryCapture;
763
- const memoryIn = src.memory;
764
851
  const settingsIn = src.settings;
765
852
  if (present(settingsIn) && (typeof settingsIn !== 'object' || Array.isArray(settingsIn))) {
766
853
  throw new TypeError(`buildTaskRequest: settings 只接受对象或缺席(undefined/null)—— 收到 ${coarseShape(settingsIn)}。` +
@@ -772,62 +859,68 @@ export function assembleTaskRequest(input, lane) {
772
859
  if (host === undefined) {
773
860
  throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(hostIn) })));
774
861
  }
775
- const resolvedIn = resolvedHookRan ? resolvedInEarly : host.carried.resolved;
862
+ const intake = resolvedHookRan ? intakeEarly : hostIntakeSnapshotOf(host, materializeAtRead(host));
863
+ if (intake === undefined)
864
+ throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(hostIn) })));
865
+ const resolvedIn = resolvedInEarly;
776
866
  const resolvedRoute = SETTINGS_KEY_ROUTES.resolved;
777
- const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live) === undefined;
778
- if (resolvedOpen && resolvedHookRan && resolvedMaterializeError !== undefined)
867
+ const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live, origin) === undefined;
868
+ if (resolvedOpen && resolvedMaterializeError !== undefined)
779
869
  throw resolvedMaterializeError;
780
- if (resolvedOpen && !resolvedHookRan)
781
- rethrowHostResolvedDeferred(host);
782
870
  const resolvedWire = resolvedOpen ? (resolvedMaterialized ?? resolvedSnapshotForWire(resolvedIn)) : {};
783
871
  const maxCostUsdIn = src.maxCostUsd;
784
872
  const excludeAllToolsIn = src.excludeAllTools;
785
873
  const maxTokensIn = src.maxTokens;
786
874
  const outputRetriesIn = src.outputRetries;
787
- const fileHistoryIn = src.fileHistory;
788
875
  const inheritEnvIn = src.inheritEnv;
789
876
  const requireExistingSessionIn = src.requireExistingSession;
790
877
  const disableSkillsIn = src.disableSkills;
791
878
  const approvalReachIn = src.approvalReach;
792
879
  const allowToolsIn = src.allowTools;
880
+ const projectMemoryIn = src.projectMemory;
793
881
  const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
794
882
  const i = snapshotByName(src, INPUT_KEYS, {
795
883
  live,
796
884
  memoryCapture: memoryCaptureIn,
797
- memory: memoryIn,
798
885
  maxCostUsd: maxCostUsdIn,
799
886
  maxTokens: maxTokensIn,
800
887
  excludeAllTools: excludeAllToolsIn,
801
888
  outputRetries: outputRetriesIn,
802
- fileHistory: fileHistoryIn,
803
889
  inheritEnv: inheritEnvIn,
804
890
  requireExistingSession: requireExistingSessionIn,
805
891
  disableSkills: disableSkillsIn,
806
892
  approvalReach: approvalReachIn,
807
893
  allowTools: allowToolsIn,
894
+ projectMemory: projectMemoryIn,
808
895
  settings: settingsIn,
809
896
  });
810
897
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
811
898
  const hostSettingsKeysIn = HOST_SETTINGS_SUBKEYS.filter((k) => hasOwn(settingsObj, k) && present(Reflect.get(settingsObj, k)));
899
+ const hostTopKeysIn = HOST_TOP_KEYS.filter((k) => hasOwn(src, k) && present(Reflect.get(src, k)));
900
+ const approvalFamiliesReadingIn = i.approvalFrameFamiliesReading;
901
+ const familiesJudged = present(approvalFamiliesReadingIn)
902
+ ? frameFamiliesJudgementOf(approvalFamiliesReadingIn)
903
+ : undefined;
904
+ const approvalFamilies = familiesJudged?.ok === true ? familiesJudged.families : undefined;
812
905
  const s = {
813
906
  resolved: resolvedIn,
814
- webSearch: host.carried.webSearch,
907
+ webSearch: intake.carried.webSearch,
815
908
  ultracode: snapshotByName(settingsObj, ['ultracode']).ultracode,
816
- hooks: host.carried.hooks,
817
- outputStyle: host.carried.outputStyle,
909
+ hooks: intake.carried.hooks,
910
+ outputStyle: intake.carried.outputStyle,
911
+ approvalFrameFamilies: approvalFamilies,
818
912
  };
819
913
  refuseBadMemoryCapture(memoryCaptureIn);
820
- refuseBadMemoryOff(memoryIn);
821
914
  refuseBadMaxCostUsd(maxCostUsdIn);
822
915
  refuseBadExcludeAllTools(excludeAllToolsIn);
823
916
  refuseBadMaxTokens(maxTokensIn);
824
917
  refuseBadOutputRetries(outputRetriesIn);
825
- refuseBadFileHistory(fileHistoryIn);
826
918
  refuseBadInheritEnv(inheritEnvIn);
827
919
  refuseBadRequireExistingSession(requireExistingSessionIn);
828
920
  refuseBadDisableSkills(disableSkillsIn, 'buildTaskRequest');
829
921
  refuseBadApprovalReach(approvalReachIn, 'buildTaskRequest');
830
922
  refuseBadAllowTools(allowToolsIn);
923
+ refuseBadProjectMemory(projectMemoryIn);
831
924
  refuseBadUserInvokedSkills(i.userInvokedSkills);
832
925
  const rewindRoute = INPUT_KEY_ROUTES.rewind;
833
926
  const RETIRED_REWIND_FILES_KEY = 'rewindFiles';
@@ -849,34 +942,36 @@ export function assembleTaskRequest(input, lane) {
849
942
  const rewindForGate = present(rewindIn) ? rewindWire : rewindIn;
850
943
  const omitted = [];
851
944
  const omit = (key, cause) => {
852
- omitted.push(Object.freeze({ key, cause }));
945
+ appendOwnElement(omitted, Object.freeze({ key, cause }));
853
946
  };
854
947
  const unknownKeys = [];
855
948
  const lateOmitted = [];
856
949
  for (const key of Object.keys(src)) {
857
950
  if (hasOwn(INPUT_KEY_ROUTES, key))
858
951
  continue;
952
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key))
953
+ continue;
859
954
  const stray = Reflect.get(src, key);
860
955
  if (!present(stray))
861
956
  continue;
862
957
  const route = routeOfTopLevelKey(key);
863
958
  if (route === undefined) {
864
- unknownKeys.push(key);
959
+ appendOwnElement(unknownKeys, key);
865
960
  continue;
866
961
  }
867
962
  if (route.via !== 'matrix' && route.via !== 'fixed')
868
963
  continue;
869
- const cause = omissionCauseOf(standingOf(route), lane, live);
964
+ const cause = omissionCauseOf(standingOf(route), lane, live, origin);
870
965
  if (cause !== undefined)
871
- lateOmitted.push(Object.freeze({ key, cause }));
966
+ appendOwnElement(lateOmitted, Object.freeze({ key, cause }));
872
967
  }
873
968
  for (const sub of Object.keys(settingsObj)) {
874
- if (hasOwn(SETTINGS_KEY_ROUTES, sub))
969
+ if (hasOwn(SETTINGS_KEY_ROUTES, sub) && !MINTED_SETTINGS_SUBKEYS.includes(sub))
875
970
  continue;
876
971
  const stray = Reflect.get(settingsObj, sub);
877
972
  if (!present(stray))
878
973
  continue;
879
- unknownKeys.push(`settings.${sub}`);
974
+ appendOwnElement(unknownKeys, `settings.${sub}`);
880
975
  }
881
976
  let retiredRewindFiles = false;
882
977
  for (const sub of Object.keys(rewindObj)) {
@@ -887,7 +982,7 @@ export function assembleTaskRequest(input, lane) {
887
982
  continue;
888
983
  if (sub === RETIRED_REWIND_FILES_KEY)
889
984
  retiredRewindFiles = true;
890
- unknownKeys.push(`rewind.${sub}`);
985
+ appendOwnElement(unknownKeys, `rewind.${sub}`);
891
986
  }
892
987
  if (retiredRewindFiles) {
893
988
  throw new TypeError('buildTaskRequest: "rewind.rewindFiles" 是退役键(引擎 core 6.0.0 起退役:「逐轮捕获」那一义被引擎忽略 —— 文件首触追踪恒开,' +
@@ -895,10 +990,10 @@ export function assembleTaskRequest(input, lane) {
895
990
  '处置:删掉这个键;要在回退会话时一并还原文件,改交 ' +
896
991
  'rewind.restoreFiles: true(必须与 rewind.resumeAt 同交);只还原文件、不动会话,交 rewind.rewindFilesTo。错误文本只报键名,不回显值。');
897
992
  }
898
- if (hostSettingsKeysIn.length > 0) {
993
+ if (hostSettingsKeysIn.length > 0 || hostTopKeysIn.length > 0) {
899
994
  throw new TypeError(renderMessage(msg('config.taskRequest.hostSuppliedKeysByHand', {
900
- keys: hostSettingsKeysIn.map((k) => `settings.${k}`).join(', '),
901
- fields: hostSettingsKeysIn.map((k) => `${k}: …`).join(', '),
995
+ keys: [...hostSettingsKeysIn.map((k) => `settings.${k}`), ...hostTopKeysIn].join(', '),
996
+ fields: [...hostSettingsKeysIn, ...hostTopKeysIn].map((k) => `${k}: …`).join(', '),
902
997
  })));
903
998
  }
904
999
  if (unknownKeys.length > 0) {
@@ -912,19 +1007,19 @@ export function assembleTaskRequest(input, lane) {
912
1007
  '错误文本只报键名,不回显值。');
913
1008
  }
914
1009
  refuseRequireExistingWithoutSession(requireExistingSessionIn, i.sessionId);
915
- refuseDisableSkillsConflict(disableSkillsIn, i.skills, Reflect.get(src, 'agents'), 'buildTaskRequest');
1010
+ refuseDisableSkillsConflict(disableSkillsIn, i.skills, Reflect.get(src, 'agents'), 'buildTaskRequest', i.userInvokedSkills);
916
1011
  refuseUtilityWithoutToolUnload(lane, excludeAllToolsIn);
917
- if (host.verdict.kind === 'refuse') {
918
- const refusing = hostRefusalNoticeOf(host, lane, live, host.verdict.notice);
1012
+ if (intake.refuses) {
1013
+ const refusing = hostRefusalNoticeOf(intake, lane, live, origin);
919
1014
  if (refusing !== undefined)
920
- throw new HostSettingsRefused(refusing);
1015
+ throw hostSettingsRefusedError(refusing, intake, intake.refusingKeys.find((k) => hostRefuseRowSeated(k, lane, live, origin)));
921
1016
  }
922
1017
  const carries = (route, value, key) => {
923
1018
  if (route.via === 'always')
924
1019
  return present(value);
925
1020
  if (route.via === 'control' || route.via === 'settings')
926
1021
  return false;
927
- const cause = omissionCauseOf(standingOf(route), lane, live);
1022
+ const cause = omissionCauseOf(standingOf(route), lane, live, origin);
928
1023
  if (!inPlay(key, value))
929
1024
  return false;
930
1025
  if (cause === undefined)
@@ -933,15 +1028,25 @@ export function assembleTaskRequest(input, lane) {
933
1028
  return false;
934
1029
  };
935
1030
  const gate = Object.create(null);
936
- for (const key of INPUT_KEYS) {
1031
+ const askInput = (key) => {
937
1032
  const route = INPUT_KEY_ROUTES[key];
938
1033
  if (route.via === 'settings') {
939
1034
  for (const sub of SETTINGS_KEYS)
940
1035
  gate[`settings.${sub}`] = carries(SETTINGS_KEY_ROUTES[sub], s[sub], `settings.${sub}`);
941
- continue;
1036
+ return;
942
1037
  }
943
1038
  gate[key] = carries(route, key === 'rewind' ? rewindForGate : i[key], key);
944
- }
1039
+ };
1040
+ const anchorAt = INPUT_KEYS.indexOf(HOST_TOP_ASK_AFTER);
1041
+ const hostTopAt = anchorAt < 0 ? INPUT_KEYS.length : anchorAt + 1;
1042
+ for (const key of INPUT_KEYS.slice(0, hostTopAt))
1043
+ askInput(key);
1044
+ const hostTopGate = {
1045
+ memory: carries(HOST_TOP_KEY_ROUTES.memory, intake.carried.memory, 'memory'),
1046
+ fileHistory: carries(HOST_TOP_KEY_ROUTES.fileHistory, intake.carried.fileHistory, 'fileHistory'),
1047
+ };
1048
+ for (const key of INPUT_KEYS.slice(hostTopAt))
1049
+ askInput(key);
945
1050
  const on = (key) => gate[key] === true;
946
1051
  const onSetting = (sub) => gate[`settings.${sub}`] === true;
947
1052
  const settingsOut = {
@@ -950,6 +1055,7 @@ export function assembleTaskRequest(input, lane) {
950
1055
  ...(onSetting('ultracode') ? { ultracode: s.ultracode } : {}),
951
1056
  ...(onSetting('hooks') ? { hooks: s.hooks } : {}),
952
1057
  ...(s.outputStyle !== undefined ? { outputStyle: s.outputStyle } : {}),
1058
+ ...(onSetting('approvalFrameFamilies') ? { approvalFrameFamilies: [...(s.approvalFrameFamilies ?? [])] } : {}),
953
1059
  };
954
1060
  const attachmentsForWire = on('attachments') ? withoutRetiredAttachmentsKeys(i.attachments) : undefined;
955
1061
  const out = {
@@ -989,18 +1095,22 @@ export function assembleTaskRequest(input, lane) {
989
1095
  ...(on('maxCostUsd') ? { maxCostUsd: maxCostUsdIn } : {}),
990
1096
  ...(on('maxTokens') ? { maxTokens: i.maxTokens } : {}),
991
1097
  ...(on('memoryCapture') ? { memoryCapture: 'off' } : {}),
992
- ...(on('memory') ? { memory: 'off' } : {}),
993
- ...(on('fileHistory') ? { fileHistory: FILE_HISTORY_DISABLED } : {}),
1098
+ ...(hostTopGate.memory ? { memory: 'off' } : {}),
1099
+ ...(hostTopGate.fileHistory ? { fileHistory: FILE_HISTORY_DISABLED } : {}),
994
1100
  ...(on('inheritEnv') ? { inheritEnv: inheritEnvIn } : {}),
995
1101
  ...(on('requireExistingSession') ? { requireExistingSession: true } : {}),
996
1102
  ...(on('disableSkills') ? { disableSkills: true } : {}),
997
1103
  ...(on('approvalReach') ? { approvalReach: approvalReachIn } : {}),
1104
+ ...(on('projectMemory') ? { projectMemory: PROJECT_MEMORY_DISABLED } : {}),
998
1105
  ...(Object.keys(settingsOut).length > 0 ? { settings: settingsOut } : {}),
999
1106
  };
1000
- return Object.freeze({ request: out, omitted: Object.freeze([...omitted, ...lateOmitted]), host });
1107
+ const familiesRoute = SETTINGS_KEY_ROUTES.approvalFrameFamilies;
1108
+ const familiesSeated = (familiesRoute.via === 'matrix' || familiesRoute.via === 'fixed') && omissionCauseOf(standingOf(familiesRoute), lane, live, origin) === undefined;
1109
+ 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 } : {}) });
1001
1111
  }
1002
- export function buildTaskRequest(input, lane) {
1003
- return assembleTaskRequest(input, lane).request;
1112
+ export function buildTaskRequest(input, laneOrIdentity) {
1113
+ return assembleTaskRequest(input, laneOrIdentity).request;
1004
1114
  }
1005
1115
  export function applyLiveRequestDefaults(req, host) {
1006
1116
  const out = { ...req };
@@ -1018,7 +1128,7 @@ export function applyLiveRequestDefaults(req, host) {
1018
1128
  out.retainSubagentSessions = true;
1019
1129
  if (out.agents === undefined && host.agents !== undefined)
1020
1130
  out.agents = host.agents;
1021
- refuseDisableSkillsConflict(out.disableSkills, out.skills, out.agents, 'applyLiveRequestDefaults');
1131
+ refuseDisableSkillsConflict(out.disableSkills, out.skills, out.agents, 'applyLiveRequestDefaults', Array.isArray(out.userInvokedSkills) ? out.userInvokedSkills : undefined);
1022
1132
  if (typeof out.scenario === 'string' && host.mockScenarioKeys?.has(out.scenario)) {
1023
1133
  delete out.scenario;
1024
1134
  }
@@ -1075,25 +1185,25 @@ export function unregisteredRequestKeys(req, lane) {
1075
1185
  if (settings === null || typeof settings !== 'object')
1076
1186
  continue;
1077
1187
  if (typeof settings.toJSON === 'function')
1078
- out.push('settings.toJSON');
1188
+ appendOwnElement(out, 'settings.toJSON');
1079
1189
  for (const [sub, value] of Object.entries(settings)) {
1080
1190
  if (sub === 'toJSON')
1081
1191
  continue;
1082
1192
  if (typeof value === 'function') {
1083
- out.push(`settings.${sub}`);
1193
+ appendOwnElement(out, `settings.${sub}`);
1084
1194
  continue;
1085
1195
  }
1086
1196
  if (laneSettingsKeys.has(sub) || liveDefaults.has(`settings.${sub}`))
1087
1197
  continue;
1088
1198
  if (settingsIsOpenSet && !foreignSettingsKeys.has(sub))
1089
1199
  continue;
1090
- out.push(`settings.${sub}`);
1200
+ appendOwnElement(out, `settings.${sub}`);
1091
1201
  }
1092
1202
  continue;
1093
1203
  }
1094
1204
  if (laneTopKeys.has(key) || liveDefaults.has(key))
1095
1205
  continue;
1096
- out.push(key);
1206
+ appendOwnElement(out, key);
1097
1207
  }
1098
1208
  return out;
1099
1209
  }