@sema-agent/client-core 0.105.0 → 0.106.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 (132) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/README.md +26 -11
  3. package/dist/adapt/arms.js +10 -1
  4. package/dist/adapter/activeRunSelfHeal.d.ts +13 -2
  5. package/dist/adapter/activeRunSelfHeal.js +80 -7
  6. package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
  7. package/dist/adapter/downstream/eventToSdkMessage.js +20 -19
  8. package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
  9. package/dist/adapter/downstream/wiringManifestView.d.ts +1 -3
  10. package/dist/adapter/downstream/wiringManifestView.js +0 -1
  11. package/dist/adapter/runStream.js +3 -4
  12. package/dist/approvalReachWire.d.ts +1 -0
  13. package/dist/approvalReachWire.js +4 -1
  14. package/dist/approvalSeat.d.ts +21 -0
  15. package/dist/approvalSeat.js +73 -0
  16. package/dist/assistantInbox.d.ts +2 -0
  17. package/dist/assistantInbox.js +3 -0
  18. package/dist/checkpointRowVerdict.d.ts +23 -0
  19. package/dist/checkpointRowVerdict.js +89 -0
  20. package/dist/coreValuePorts.d.ts +12 -1
  21. package/dist/coreValuePorts.js +10 -0
  22. package/dist/decideFailureNote.js +3 -1
  23. package/dist/decideReceipt.d.ts +9 -0
  24. package/dist/decideReceipt.js +29 -1
  25. package/dist/declared/supportFloorDeclared.js +2 -2
  26. package/dist/deviceRebind.d.ts +7 -0
  27. package/dist/deviceRebind.js +39 -5
  28. package/dist/engineAddress.d.ts +3 -0
  29. package/dist/engineAddress.js +25 -0
  30. package/dist/engineDirectory.js +9 -17
  31. package/dist/engineErrorCodes.d.ts +8 -0
  32. package/dist/engineErrorCodes.js +8 -0
  33. package/dist/engineNoticeCodes.d.ts +26 -3
  34. package/dist/engineNoticeCodes.js +52 -7
  35. package/dist/engineRowRef.js +1 -1
  36. package/dist/errorSupplementRoles.js +2 -0
  37. package/dist/eventLeg.d.ts +2 -2
  38. package/dist/eventLeg.js +2 -1
  39. package/dist/fleet/fleetProjection.js +26 -5
  40. package/dist/generated/engineFactTables.d.ts +2 -1
  41. package/dist/generated/engineFactTables.js +3 -1
  42. package/dist/generated/engineNoticeTables.js +19 -4
  43. package/dist/generated/sdkFactTables.d.ts +3 -0
  44. package/dist/generated/sdkFactTables.js +15 -0
  45. package/dist/generated/settingsSchemaFns.d.ts +11 -0
  46. package/dist/generated/settingsSchemaFns.js +2 -1
  47. package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
  48. package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
  49. package/dist/generated/settingsSchemaSettingsSources.js +22 -2
  50. package/dist/hitl/approvalLiveFace.d.ts +17 -0
  51. package/dist/hitl/approvalLiveFace.js +35 -0
  52. package/dist/hitl/approvalQueueRow.d.ts +21 -0
  53. package/dist/hitl/approvalQueueRow.js +69 -0
  54. package/dist/hitl/approvalsFeed.d.ts +5 -4
  55. package/dist/hitl/approvalsFeed.js +38 -6
  56. package/dist/hitl/askParkRowRouting.d.ts +4 -3
  57. package/dist/hitl/askParkRowRouting.js +15 -3
  58. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  59. package/dist/hitl/crashConverged.d.ts +5 -1
  60. package/dist/hitl/decisionConflict.js +2 -1
  61. package/dist/hitl/decisionRefusal.js +1 -1
  62. package/dist/hitl/decisionSend.d.ts +1 -0
  63. package/dist/hitl/decisionSend.js +14 -2
  64. package/dist/hitl/frameRouter.js +4 -5
  65. package/dist/hitl/hitlBridge.d.ts +1 -1
  66. package/dist/hitl/hitlBridge.js +26 -5
  67. package/dist/hitl/livePendingAsk.d.ts +5 -0
  68. package/dist/hitl/livePendingAsk.js +6 -1
  69. package/dist/hitl/planReviewCard.d.ts +2 -0
  70. package/dist/hitl/planReviewCard.js +21 -0
  71. package/dist/hitl/planReviewWire.d.ts +2 -2
  72. package/dist/hitl/planReviewWire.js +101 -36
  73. package/dist/hitl/sessionAllowRoute.d.ts +2 -0
  74. package/dist/hitl/sessionAllowRoute.js +7 -0
  75. package/dist/hitl/sessionPolicyWire.d.ts +1 -7
  76. package/dist/hitl/sessionPolicyWire.js +3 -43
  77. package/dist/hitl/subagentSource.d.ts +1 -0
  78. package/dist/hitl/subagentSource.js +13 -0
  79. package/dist/hitl/toolApprovalWire.d.ts +3 -2
  80. package/dist/hitl/toolApprovalWire.js +79 -37
  81. package/dist/hostSettingsSource.d.ts +4 -1
  82. package/dist/hostSettingsSource.js +11 -6
  83. package/dist/hostTurnSettings.d.ts +4 -5
  84. package/dist/hostTurnSettings.js +80 -19
  85. package/dist/i18n/messages.en.d.ts +35 -12
  86. package/dist/i18n/messages.en.js +36 -13
  87. package/dist/i18n/messages.zh.js +38 -13
  88. package/dist/index.d.ts +21 -2
  89. package/dist/index.js +11 -2
  90. package/dist/liveInitToolFace.js +0 -1
  91. package/dist/liveModelCatalog.d.ts +15 -0
  92. package/dist/liveModelCatalog.js +40 -0
  93. package/dist/mcpWireCaps.js +1 -1
  94. package/dist/modelCapabilityProbe.js +66 -33
  95. package/dist/ownKey.d.ts +0 -1
  96. package/dist/ownKey.js +0 -2
  97. package/dist/permissionsDeclaration.d.ts +2 -2
  98. package/dist/permissionsDeclaration.js +12 -14
  99. package/dist/projectSeed.d.ts +39 -0
  100. package/dist/projectSeed.js +80 -0
  101. package/dist/promptDelivery.js +1 -1
  102. package/dist/replayBody.d.ts +7 -6
  103. package/dist/replayBody.js +62 -7
  104. package/dist/request/taskRequest.js +3 -4
  105. package/dist/ruleCompileVerdict.d.ts +0 -2
  106. package/dist/ruleCompileVerdict.js +0 -31
  107. package/dist/ruleRefusalSentence.js +0 -3
  108. package/dist/runReattachPolicy.d.ts +3 -0
  109. package/dist/runReattachPolicy.js +15 -2
  110. package/dist/runTerminal.js +30 -8
  111. package/dist/sandboxWire.d.ts +0 -3
  112. package/dist/sandboxWire.js +0 -9
  113. package/dist/seam.d.ts +14 -7
  114. package/dist/seam.js +12 -0
  115. package/dist/seatContract.d.ts +57 -7
  116. package/dist/seatContract.js +132 -3
  117. package/dist/serverSupportRange.d.ts +18 -0
  118. package/dist/serverSupportRange.js +25 -0
  119. package/dist/sessionEngine.d.ts +156 -0
  120. package/dist/sessionEngine.js +464 -0
  121. package/dist/sessionMap.d.ts +11 -1
  122. package/dist/sessionMap.js +35 -9
  123. package/dist/sessionMemorySync.d.ts +31 -0
  124. package/dist/sessionMemorySync.js +95 -0
  125. package/dist/sessionRowTag.d.ts +4 -0
  126. package/dist/sessionRowTag.js +18 -0
  127. package/dist/settingsLayerAdmission.d.ts +1 -0
  128. package/dist/settingsLayerAdmission.js +4 -1
  129. package/dist/toolResult.js +1 -1
  130. package/dist/wireRefusalCopy.js +2 -6
  131. package/docs/INTEGRATION-CLIENTS.md +1022 -16
  132. package/package.json +5 -5
@@ -15,9 +15,10 @@ import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
15
15
  import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
16
16
  import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
17
17
  import { msg, renderMessage } from '../i18n/translate.js';
18
+ import { sessionAllowCarried } from './sessionAllowRoute.js';
18
19
  import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
19
20
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
20
- import { displayUntrusted } from '../displayUntrusted.js';
21
+ import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
21
22
  import { frameBindingOf } from '../frameBinding.js';
22
23
  import { carriesAttributionKey, deliverWithAttributionLadder } from './attributionLadder.js';
23
24
  import { deliverApprovalDecision, newSendTally } from './approvalDelivery.js';
@@ -28,6 +29,8 @@ import { isApprovalMandateWord } from '../gateVocabulary.js';
28
29
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
29
30
  import { decideFailureNote } from '../decideFailureNote.js';
30
31
  import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
32
+ import { APPROVAL_LIVE_FACE_KEYS, approvalLiveFaceOf, hasOwnStrictTrue } from './approvalLiveFace.js';
33
+ import { subagentSourceSessionOf } from './subagentSource.js';
31
34
  import { isPlainRecord } from '../jsonFaithful.js';
32
35
  import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
33
36
  import { readDecideReceipt } from '../decideReceipt.js';
@@ -156,7 +159,6 @@ function cardPortMissReason() {
156
159
  }
157
160
  const REMEMBER_REFUSAL_CODES = Object.freeze([
158
161
  'remember_not_in_proof',
159
- 'remember_requires_binding',
160
162
  'capability.store_required',
161
163
  DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED,
162
164
  'decide.parked_remember_unsupported',
@@ -512,9 +514,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
512
514
  ...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
513
515
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
514
516
  ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
515
- ...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
516
- ...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(pending)),
517
- ...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(pending)),
517
+ ...approvalLiveFaceOf(pending).facts,
518
518
  ...((w) => (w !== undefined ? { approvalCard: w } : {}))(readApprovalCardWord(pending)),
519
519
  ...(readAskClassPair(pending) ?? {}),
520
520
  ...(pending.gateKind === 'irreversible_ask' ? { irreversibleParkGate: true } : {}),
@@ -683,6 +683,9 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
683
683
  'toolCallId',
684
684
  'toolName',
685
685
  'sourceTaskId',
686
+ 'sourceSessionId',
687
+ 'agentId',
688
+ 'askReasons',
686
689
  'fromSubagent',
687
690
  'sourceAgentName',
688
691
  'message',
@@ -758,9 +761,6 @@ export function toolPermissionRequestAskBits(card, requestId) {
758
761
  ...(seatSessionRowOf(card, requestId) ? { sessionRow: true } : {}),
759
762
  };
760
763
  }
761
- function hasOwnStrictTrue(v, key) {
762
- return Object.hasOwn(v, key) && v[key] === true;
763
- }
764
764
  function isToolApprovalDelegation(v) {
765
765
  if (v === null || typeof v !== 'object')
766
766
  return false;
@@ -1283,6 +1283,30 @@ export function classifierNoRulingDetail(word, lang) {
1283
1283
  return renderMessage(msg('approval.classifier.noRulingOther', { reason: displayUntrusted(word) }), lang);
1284
1284
  return renderMessage(msg('approval.classifier.noRulingBare'), lang);
1285
1285
  }
1286
+ const BOUND_GRANT_NAMES_SHOWN = 6;
1287
+ const BOUND_GRANT_NAME_MAX = 512;
1288
+ const BOUND_GRANT_NAME_INPUT_MAX = 8192;
1289
+ const BOUND_GRANT_NAMES_LOOKAHEAD = 32;
1290
+ function boundGrantNameList(rules, lang) {
1291
+ const shown = [];
1292
+ let looked = 0;
1293
+ for (const entry of rules) {
1294
+ if (shown.length >= BOUND_GRANT_NAMES_SHOWN || looked >= BOUND_GRANT_NAMES_LOOKAHEAD)
1295
+ break;
1296
+ looked += 1;
1297
+ if (entry.length > BOUND_GRANT_NAME_INPUT_MAX)
1298
+ continue;
1299
+ const quoted = quoteUntrustedForSentence(entry, BOUND_GRANT_NAME_MAX);
1300
+ if (quoted !== undefined)
1301
+ shown.push(quoted);
1302
+ }
1303
+ if (shown.length === 0)
1304
+ return undefined;
1305
+ const rest = rules.length - shown.length;
1306
+ if (rest > 0)
1307
+ shown.push(renderMessage(msg('approval.boundGrants.more', { count: rest }), lang));
1308
+ return shown.join(' · ');
1309
+ }
1286
1310
  export function approvalBoundGrantsDetail(grants, lang) {
1287
1311
  const g = readApprovalBoundGrants({ boundGrants: grants });
1288
1312
  if (g === undefined)
@@ -1290,8 +1314,10 @@ export function approvalBoundGrantsDetail(grants, lang) {
1290
1314
  if (g.skill === null)
1291
1315
  return renderMessage(msg('approval.boundGrants.noSkill'), lang);
1292
1316
  const isRule = (e) => readBoundGrantEntry(e)?.kind === 'rule';
1293
- const a = g.allowedTools.filter(isRule).length;
1294
- const d = g.disallowedTools.filter(isRule).length;
1317
+ const allowRules = g.allowedTools.filter(isRule);
1318
+ const denyRules = g.disallowedTools.filter(isRule);
1319
+ const a = allowRules.length;
1320
+ const d = denyRules.length;
1295
1321
  const allowNothing = g.allowedTools.length - a;
1296
1322
  const denyNothing = g.disallowedTools.length - d;
1297
1323
  if (denyNothing > 0) {
@@ -1302,8 +1328,14 @@ export function approvalBoundGrantsDetail(grants, lang) {
1302
1328
  return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.listsNoToolsWithNothing.one') : msg('approval.boundGrants.listsNoToolsWithNothing.other', { count: String(allowNothing) }), lang);
1303
1329
  return renderMessage(msg('approval.boundGrants.listsNoTools'), lang);
1304
1330
  }
1305
- const allow = a === 1 ? msg('approval.boundGrants.allowPart.one') : msg('approval.boundGrants.allowPart.other', { count: a });
1306
- const deny = d === 1 ? msg('approval.boundGrants.denyPart.one') : msg('approval.boundGrants.denyPart.other', { count: d });
1331
+ const allowNames = boundGrantNameList(allowRules, lang);
1332
+ const denyNames = boundGrantNameList(denyRules, lang);
1333
+ const allow = allowNames !== undefined
1334
+ ? (a === 1 ? msg('approval.boundGrants.allowPart.named.one', { names: allowNames }) : msg('approval.boundGrants.allowPart.named.other', { count: a, names: allowNames }))
1335
+ : (a === 1 ? msg('approval.boundGrants.allowPart.one') : msg('approval.boundGrants.allowPart.other', { count: a }));
1336
+ const deny = denyNames !== undefined
1337
+ ? (d === 1 ? msg('approval.boundGrants.denyPart.named.one', { names: denyNames }) : msg('approval.boundGrants.denyPart.named.other', { count: d, names: denyNames }))
1338
+ : (d === 1 ? msg('approval.boundGrants.denyPart.one') : msg('approval.boundGrants.denyPart.other', { count: d }));
1307
1339
  if (allowNothing > 0) {
1308
1340
  return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.activatesWithNothing.one', { allow, deny }) : msg('approval.boundGrants.activatesWithNothing.other', { allow, deny, count: String(allowNothing) }), lang);
1309
1341
  }
@@ -1320,7 +1352,8 @@ function askSigOf(classes, origin, mandate) {
1320
1352
  }
1321
1353
  function carrierAskSig(v, live) {
1322
1354
  try {
1323
- return askSigOf(readAskClassPair(v)?.classes, live === undefined ? undefined : readAskOrigin(live), live === undefined ? undefined : readApprovalMandate(live));
1355
+ const face = live === undefined ? undefined : approvalLiveFaceOf(live);
1356
+ return askSigOf(readAskClassPair(v)?.classes, face?.facts.origin, face?.facts.mandate);
1324
1357
  }
1325
1358
  catch {
1326
1359
  return undefined;
@@ -1605,10 +1638,11 @@ export function readRuleOfferSupply(offers) {
1605
1638
  }
1606
1639
  function subagentBadgeFor(frame) {
1607
1640
  let name = typeof frame.sourceAgentName === 'string' && frame.sourceAgentName.length > 0 ? frame.sourceAgentName : '';
1608
- if (!name && typeof frame.sourceTaskId === 'string' && frame.sourceTaskId.length > 0) {
1609
- name = frame.sourceTaskId;
1641
+ const source = subagentSourceSessionOf(frame);
1642
+ if (!name && source !== undefined) {
1643
+ name = source;
1610
1644
  try {
1611
- const row = readEngineActiveBgTasks().tasks.find(t => t.id === frame.sourceTaskId);
1645
+ const row = readEngineActiveBgTasks().tasks.find(t => t.id === source);
1612
1646
  if (typeof row?.name === 'string' && row.name.length > 0)
1613
1647
  name = row.name;
1614
1648
  }
@@ -1631,10 +1665,11 @@ function v1Bit(stamp) {
1631
1665
  const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1632
1666
  'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1633
1667
  'Edits are not accepted on this card: there is no original input to edit';
1634
- function cardFactsOf(carrier, leg, opts, liveFacts) {
1668
+ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
1635
1669
  const c = carrier;
1636
1670
  const full = leg !== 'generic';
1637
1671
  const live = leg === 'frame' ? carrier : leg === 'v1' ? liveFacts : undefined;
1672
+ let face;
1638
1673
  const asFrame = carrier;
1639
1674
  const fromSubagent = full
1640
1675
  ? (() => {
@@ -1747,15 +1782,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts) {
1747
1782
  realApprovalUnreadable = true;
1748
1783
  }
1749
1784
  if (live !== undefined) {
1750
- v1Bit(() => {
1751
- if (hasOwnStrictTrue(live, 'mandated'))
1752
- out.mandated = true;
1753
- });
1754
- v1Bit(() => {
1755
- const w = readApprovalMandate(live);
1756
- if (w !== undefined)
1757
- out.mandate = w;
1758
- });
1785
+ face = approvalLiveFaceOf(live);
1786
+ if (face.facts.mandated === true)
1787
+ out.mandated = true;
1788
+ if (face.facts.mandate !== undefined)
1789
+ out.mandate = face.facts.mandate;
1759
1790
  v1Bit(() => {
1760
1791
  const rc = readReadRootCandidate(live.readRootCandidate);
1761
1792
  if (rc !== undefined)
@@ -1777,13 +1808,8 @@ function cardFactsOf(carrier, leg, opts, liveFacts) {
1777
1808
  if (dlf !== undefined)
1778
1809
  out.denialLimitFallback = dlf;
1779
1810
  });
1780
- if (live !== undefined) {
1781
- v1Bit(() => {
1782
- const o = readAskOrigin(live);
1783
- if (o !== undefined)
1784
- out.origin = o;
1785
- });
1786
- }
1811
+ if (face !== undefined && face.facts.origin !== undefined)
1812
+ out.origin = face.facts.origin;
1787
1813
  if (leg !== 'generic') {
1788
1814
  v1Bit(() => {
1789
1815
  const w = readApprovalCardWord(carrier);
@@ -1817,16 +1843,32 @@ function cardFactsOf(carrier, leg, opts, liveFacts) {
1817
1843
  });
1818
1844
  if (leg === 'generic')
1819
1845
  out.cardPayloadUnreadable = true;
1820
- if (leg === 'frame')
1846
+ if (sessionAllowCarried(leg, approvalId))
1821
1847
  out.allowSessionCarried = true;
1822
1848
  out.grantBinding = readApprovalGrantBinding(carrier);
1823
- return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : [], live);
1849
+ const reported = realApprovalUnreadable ? ['requiresRealApproval'] : [];
1850
+ if (face?.layerUnknown === 'marked' || (leg === 'v1' && face?.layerUnknown === 'absent'))
1851
+ reported.push(...APPROVAL_LIVE_FACE_KEYS);
1852
+ return withUnreadableEvidence(carrier, leg, out, reported, live);
1824
1853
  }
1825
1854
  export function approvalCardFactsOfV1(card) {
1826
1855
  if (typeof card !== 'object' || card === null)
1827
1856
  return undefined;
1828
1857
  return Object.freeze(cardFactsOf(card, 'v1'));
1829
1858
  }
1859
+ export function approvalCardFactsOfV1Frame(frame) {
1860
+ if (typeof frame !== 'object' || frame === null)
1861
+ return undefined;
1862
+ try {
1863
+ const carriers = openingFrameCarriersOf(frame);
1864
+ if (carriers.leg !== 'v1')
1865
+ return undefined;
1866
+ return Object.freeze(cardFactsOf(carriers.body, 'v1', null, carriers.live, ownDataValue(frame, 'approvalId')));
1867
+ }
1868
+ catch {
1869
+ return undefined;
1870
+ }
1871
+ }
1830
1872
  export function approvalCardFactsOfFrame(frame, opts) {
1831
1873
  if (typeof frame !== 'object' || frame === null)
1832
1874
  return undefined;
@@ -1888,7 +1930,7 @@ function liveCardIdentityOf(approvalId, frame, carriers, run) {
1888
1930
  const fromSubagent = isFromSubagent(carriers.body);
1889
1931
  const bound = fromSubagent ? undefined : frameBindingOf(frame);
1890
1932
  const source = bound ?? run;
1891
- const ownRun = fromSubagent ? body.sourceTaskId : source?.taskId;
1933
+ const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : source?.taskId;
1892
1934
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1893
1935
  const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
1894
1936
  const askSig = carrierAskSig(carriers.body, carriers.live);
@@ -1950,7 +1992,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
1950
1992
  const bodyToolCallId = carriers.body.toolCallId;
1951
1993
  const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
1952
1994
  const decidedByReading = decidedByReadingSnapshotOf(lane);
1953
- const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live);
1995
+ const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId);
1954
1996
  const argsNotRealInput = facts.argsUnavailable === true;
1955
1997
  const ruleOffers = facts.ruleOffers;
1956
1998
  if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
@@ -2,7 +2,7 @@ import { SETTINGS_WORKSPACE_TRUST_STATES } from './generated/settingsSchemaTable
2
2
  import type { ManagedPolicyState, PluginHooksReading, SessionHooksReading, SettingsDocument } from './host.js';
3
3
  import type { PluginHookReaderState } from './pluginHooksWire.js';
4
4
  export type WorkspaceTrustState = (typeof SETTINGS_WORKSPACE_TRUST_STATES)[number];
5
- export declare const HOST_SETTINGS_LAYERS: readonly ["policySettings", "userSettings", "projectSettings", "localSettings", "flagSettings"];
5
+ export declare const HOST_SETTINGS_LAYERS: readonly ["policySettings", "userSettings", "projectSettings", "localSettings", "flagSettings", "foreignProjectSettings"];
6
6
  export type HostSettingsLayer = (typeof HOST_SETTINGS_LAYERS)[number];
7
7
  export declare const MANAGED_POLICY_STATES: readonly ["absent", "in-effect", "in-effect-pieces", "unreadable-covered", "unreadable-uncovered", "remote-unknown"];
8
8
  export declare const HOST_STARTUP_MODES: readonly ["safe", "bare", "none"];
@@ -71,6 +71,8 @@ export declare function sourceOfRead(read: HostSettingsRead): HostSettingsSource
71
71
  export declare function settingsValueOf(read: HostSettingsRead, key: string): {} | null | undefined;
72
72
  export declare function mergedBooleanOf(read: HostSettingsRead, key: string): boolean | undefined;
73
73
  export declare const CC_MERGE_ORDER: readonly ["userSettings", "projectSettings", "localSettings", "flagSettings", "policySettings"];
74
+ export type TightenOnlyHostSettingsLayer = Exclude<HostSettingsLayer, (typeof CC_MERGE_ORDER)[number]>;
75
+ export declare const TIGHTEN_ONLY_HOST_SETTINGS_LAYERS: readonly TightenOnlyHostSettingsLayer[];
74
76
  export declare function nonManagedLayerDown(read: HostSettingsRead): boolean;
75
77
  export declare const ADOPTED_LAYERS: readonly ["userSettings", "projectSettings", "localSettings", "flagSettings"];
76
78
  export type AdoptedLayer = (typeof ADOPTED_LAYERS)[number];
@@ -102,6 +104,7 @@ export declare function hostSettingsSessionFollows(stored: HostSettingsSession |
102
104
  export declare const FIRST_SESSION: HostSettingsSession;
103
105
  export declare function sessionSnapshotOf(previous: {} | null | undefined): HostSettingsSession | undefined;
104
106
  export declare function permissionRulesOf(doc: SettingsDocument | null, bucket: 'allow' | 'deny' | 'ask'): readonly string[];
107
+ export declare function permissionBucketOf(doc: SettingsDocument | null, bucket: 'allow' | 'deny' | 'ask'): readonly string[] | undefined;
105
108
  export interface HostSettingsAdoption {
106
109
  readonly read: HostSettingsRead;
107
110
  readonly session: HostSettingsSession;
@@ -2,7 +2,7 @@ import { appendOwnElement } from './ownKey.js';
2
2
  import { SETTINGS_WORKSPACE_TRUST_STATES } from './generated/settingsSchemaTables.js';
3
3
  import { jsonFaithfulCost, materializeJsonFaithful } from './jsonFaithful.js';
4
4
  import { faultText } from './hostCallbackGuard.js';
5
- export const HOST_SETTINGS_LAYERS = Object.freeze(['policySettings', 'userSettings', 'projectSettings', 'localSettings', 'flagSettings']);
5
+ export const HOST_SETTINGS_LAYERS = Object.freeze(['policySettings', 'userSettings', 'projectSettings', 'localSettings', 'flagSettings', 'foreignProjectSettings']);
6
6
  export const MANAGED_POLICY_STATES = Object.freeze(['absent', 'in-effect', 'in-effect-pieces', 'unreadable-covered', 'unreadable-uncovered', 'remote-unknown']);
7
7
  export const HOST_STARTUP_MODES = Object.freeze(['safe', 'bare', 'none']);
8
8
  export const HOST_SETTINGS_CARRIER_STATUSES = Object.freeze(['loaded', 'loaded-partial', 'empty', 'absent', 'unreadable', 'not-loaded']);
@@ -394,8 +394,9 @@ export function mergedBooleanOf(read, key) {
394
394
  return out;
395
395
  }
396
396
  export const CC_MERGE_ORDER = Object.freeze(['userSettings', 'projectSettings', 'localSettings', 'flagSettings', 'policySettings']);
397
+ export const TIGHTEN_ONLY_HOST_SETTINGS_LAYERS = Object.freeze(HOST_SETTINGS_LAYERS.filter((l) => !CC_MERGE_ORDER.includes(l)));
397
398
  export function nonManagedLayerDown(read) {
398
- return HOST_SETTINGS_LAYERS.some((l) => {
399
+ return CC_MERGE_ORDER.some((l) => {
399
400
  if (l === 'policySettings')
400
401
  return false;
401
402
  const snap = read.layers[l];
@@ -403,6 +404,7 @@ export function nonManagedLayerDown(read) {
403
404
  });
404
405
  }
405
406
  export const ADOPTED_LAYERS = Object.freeze(['userSettings', 'projectSettings', 'localSettings', 'flagSettings']);
407
+ const isAdoptedLayer = (l) => ADOPTED_LAYERS.includes(l);
406
408
  const GENESIS_VERSION = '0';
407
409
  export const parentVersionOf = (session) => (session.version === GENESIS_VERSION ? null : session.version);
408
410
  function canonicalOf(v) {
@@ -512,13 +514,16 @@ export function sessionSnapshotOf(previous) {
512
514
  }
513
515
  const LOADED = new Set(['loaded', 'loaded-partial']);
514
516
  export function permissionRulesOf(doc, bucket) {
517
+ return permissionBucketOf(doc, bucket) ?? [];
518
+ }
519
+ export function permissionBucketOf(doc, bucket) {
515
520
  if (doc === null)
516
- return [];
521
+ return undefined;
517
522
  const p = ownData(doc, 'permissions');
518
523
  if (!p.ok || !isRecord(p.value))
519
- return [];
524
+ return undefined;
520
525
  const b = ownData(p.value, bucket);
521
- return b.ok && Array.isArray(b.value) ? b.value.filter((x) => typeof x === 'string') : [];
526
+ return b.ok && Array.isArray(b.value) ? b.value.filter((x) => typeof x === 'string') : undefined;
522
527
  }
523
528
  function overflowOf(read, session) {
524
529
  return Object.freeze({ read, session: remintSameContent(session), trusted: false, clean: false, extraTighten: NO_EXTRA, allowByLayer: emptyByLayer(), overflow: true });
@@ -624,7 +629,7 @@ export function adoptHostSettingsRead(read, session, allowAdmitted = () => true,
624
629
  const effectiveLayers = {};
625
630
  for (const l of HOST_SETTINGS_LAYERS) {
626
631
  const snap = read.layers[l];
627
- effectiveLayers[l] = l === 'policySettings' ? snap : Object.freeze({ ...snap, doc: base.docs[l] });
632
+ effectiveLayers[l] = isAdoptedLayer(l) ? Object.freeze({ ...snap, doc: base.docs[l] }) : snap;
628
633
  }
629
634
  const effective = Object.freeze({ ...read, layers: Object.freeze(effectiveLayers), current: read, debug: Object.freeze([]) });
630
635
  const idOf = (layer, rule) => (layeredRules ? JSON.stringify([layer, rule]) : rule);
@@ -3,8 +3,8 @@ import type { WireHooksConfig } from './finalVerifyWire.js';
3
3
  import type { memoryOffDeclared as MemoryOffDeclared } from './request/taskRequest.js';
4
4
  import type { EnvLike } from './hostEnv.js';
5
5
  import { type SettingsDocument } from './host.js';
6
- import type { HookSettingsSource, HooksWirePlan, HooksWirePlanOptions } from './pluginHooksWire.js';
7
- import { type HostSettingsSession, type HostSettingsSource } from './hostSettingsSource.js';
6
+ import type { HooksWirePlan, HooksWirePlanOptions } from './pluginHooksWire.js';
7
+ import { type HostSettingsLayer, type HostSettingsSession, type HostSettingsSource } from './hostSettingsSource.js';
8
8
  import { type SubmissionIdentity } from './request/submissionIdentity.js';
9
9
  import { type WebSearchApiKeyFor } from './webSearchWireCaps.js';
10
10
  import { type FileHistoryCaptureReading } from './fileHistoryCaptureCapability.js';
@@ -16,12 +16,12 @@ export type HostSettingCause = (typeof HOST_SETTING_CAUSES)[number];
16
16
  export declare const HOST_SETTING_DISPOSITIONS: readonly ["carry", "carry_partial", "withhold_notice", "withhold_by_policy", "hold_for_consent", "refuse_turn", "unwired", "nothing_to_carry"];
17
17
  export type HostSettingDisposition = (typeof HOST_SETTING_DISPOSITIONS)[number];
18
18
  export type HostSettingPlane = 'turn' | 'session' | 'diagnostic';
19
- export type HostSettingNoticeId = 'HS-UNWIRED' | 'HS-MANAGED-REJECTED' | 'HS-PROJECTION' | 'HS-SOURCE-UNREADABLE' | 'HS-WEBSEARCH-MALFORMED' | 'HS-SWITCH-MALFORMED' | 'HS-FILEHISTORY-UNJUDGED' | 'HS-FILEHISTORY-RESTORE' | 'HS-MANAGED-UNREADABLE' | 'HS-LAYER-NOT-LOADED' | 'HS-HELD-PREVIOUS' | 'HS-STYLE-MISSING' | 'HS-STYLE-SAFE';
19
+ export type HostSettingNoticeId = 'HS-UNWIRED' | 'HS-MANAGED-REJECTED' | 'HS-PROJECTION' | 'HS-SOURCE-UNREADABLE' | 'HS-WEBSEARCH-MALFORMED' | 'HS-SWITCH-MALFORMED' | 'HS-FILEHISTORY-UNJUDGED' | 'HS-FILEHISTORY-RESTORE' | 'HS-MANAGED-UNREADABLE' | 'HS-LAYER-NOT-LOADED' | 'HS-HELD-PREVIOUS' | 'HS-STYLE-MISSING' | 'HS-STYLE-SAFE' | 'HS-LAYER-PARTS-UNREAD';
20
20
  export interface HostSettingRow {
21
21
  readonly key: HostSuppliedKey;
22
22
  readonly cause: HostSettingCause;
23
23
  readonly disposition: HostSettingDisposition;
24
- readonly layers?: readonly HookSettingsSource[];
24
+ readonly layers?: readonly HostSettingsLayer[];
25
25
  }
26
26
  export interface HostSettingNotice {
27
27
  readonly id: HostSettingNoticeId;
@@ -123,7 +123,6 @@ export interface HostPermissionFacts {
123
123
  readonly ask?: readonly string[];
124
124
  };
125
125
  readonly clientOwnedServers?: readonly string[];
126
- readonly serverVersion?: string;
127
126
  }
128
127
  export declare function rethrowHostResolvedDeferred(product: HostTurnSettings): void;
129
128
  export type HostResolvedMaterializer = (snapshot: SettingsDocument) => SettingsDocument;
@@ -1,7 +1,7 @@
1
1
  import { SETTINGS_CLASSIFY_HOOKS_CONFIG } from './generated/settingsSchemaFns.js';
2
2
  import { hostLog } from './host.js';
3
3
  import { layerNotAppliedRefusesTurn, salvageSettingsHooksEntries } from './settingsHooksSalvage.js';
4
- import { ADOPTED_LAYERS, HOST_SETTINGS_LAYERS, HostSettingsSourceShapeError, permissionRulesOf, MANAGED_READING_DISPOSITIONS, adoptHostSettingsRead, mintHostSettingsSession, readHostSettingsOnce, remintSameContent, sessionSnapshotOf, settingsValueOf, sourceOfRead, } from './hostSettingsSource.js';
4
+ import { ADOPTED_LAYERS, HOST_SETTINGS_LAYERS, HostSettingsSourceShapeError, TIGHTEN_ONLY_HOST_SETTINGS_LAYERS, permissionBucketOf, permissionRulesOf, MANAGED_READING_DISPOSITIONS, adoptHostSettingsRead, mintHostSettingsSession, readHostSettingsOnce, remintSameContent, sessionSnapshotOf, settingsValueOf, sourceOfRead, } from './hostSettingsSource.js';
5
5
  import { hooksWirePlan } from './hooksWireCaps.js';
6
6
  import { submissionSurfaceOf } from './request/submissionIdentity.js';
7
7
  import { displayUntrusted } from './displayUntrusted.js';
@@ -11,7 +11,8 @@ import { msg, renderMessage } from './i18n/translate.js';
11
11
  import { newRulePortsProjection, projectPermissionsDeclaration, rulePortsMissingOf, WIRE_RULE_TRUST_ORDER } from './permissionsDeclaration.js';
12
12
  import { assemblyRefusalOf } from './wireFailureShape.js';
13
13
  import { permissionRuleLayersOn } from './permissionsAllowGrantCapability.js';
14
- import { layerAdmitted } from './settingsLayerAdmission.js';
14
+ import { layerAdmissionReading, layerAdmitted } from './settingsLayerAdmission.js';
15
+ import { SETTINGS_BUILD_LAYER_PARTS_UNREAD_NOTICE } from './generated/settingsSchemaFns.js';
15
16
  import { appendOwnElement, ownDataField } from './ownKey.js';
16
17
  export const HOST_SUPPLIED_KEYS = Object.freeze(['settings.hooks', 'settings.resolved', 'settings.webSearch', 'settings.outputStyle', 'memory', 'fileHistory']);
17
18
  export const HOST_SETTING_CAUSES = Object.freeze([
@@ -42,6 +43,7 @@ const HOST_SETTING_NOTICE_TEXT = {
42
43
  'HS-HELD-PREVIOUS': () => renderMessage(msg('session.hostSettings.heldPrevious')),
43
44
  'HS-STYLE-MISSING': (what) => renderMessage(msg('session.hostSettings.styleMissing', { name: what })),
44
45
  'HS-STYLE-SAFE': () => renderMessage(msg('session.hostSettings.styleSafeMode')),
46
+ 'HS-LAYER-PARTS-UNREAD': (line) => line,
45
47
  };
46
48
  const KEY_NOUN = {
47
49
  'settings.hooks': 'the hooks',
@@ -367,10 +369,60 @@ function stringListOf(v) {
367
369
  function permissionMemberAdmitted(layer, read, bucket) {
368
370
  return layerAdmitted(layer, read.trust, 'permissions', bucket);
369
371
  }
372
+ function managedRulesOnly(read) {
373
+ const policyDoc = read.layers.policySettings.doc;
374
+ return MANAGED_READING_DISPOSITIONS[read.managed].ruleScope === 'by-switch' && policyDoc !== null && managedOnlySwitchOn(policyDoc);
375
+ }
376
+ const TIGHTEN_ONLY_LAYER_BUCKETS = Object.freeze(['deny', 'ask']);
377
+ function tightenOnlyBucketsRead(read, layer) {
378
+ if (managedRulesOnly(read))
379
+ return [];
380
+ const doc = read.layers[layer].doc;
381
+ return TIGHTEN_ONLY_LAYER_BUCKETS.filter((bucket) => permissionMemberAdmitted(layer, read, bucket) && permissionBucketOf(doc, bucket) !== undefined);
382
+ }
383
+ function tightenOnlyLayerPartsUnread(read, layer, permissionsRead) {
384
+ const doc = read.layers[layer].doc;
385
+ if (doc === null)
386
+ return [];
387
+ const readBuckets = permissionsRead ? tightenOnlyBucketsRead(read, layer) : [];
388
+ const parts = [];
389
+ const rec = doc;
390
+ for (const key of Object.keys(rec)) {
391
+ const v = rec[key];
392
+ if (key !== 'permissions')
393
+ appendOwnElement(parts, key);
394
+ else if (typeof v !== 'object' || v === null || Array.isArray(v))
395
+ appendOwnElement(parts, key);
396
+ else
397
+ for (const member of Object.keys(v))
398
+ if (!readBuckets.includes(member))
399
+ appendOwnElement(parts, `${key}.${member}`);
400
+ }
401
+ for (const c of read.layers[layer].carriers)
402
+ if (c.status === 'loaded-partial')
403
+ for (const k of c.issues ?? [])
404
+ if (!parts.includes(k))
405
+ appendOwnElement(parts, k);
406
+ return parts;
407
+ }
408
+ function tightenOnlyLayerNotices(read, permissionsRead) {
409
+ const out = [];
410
+ for (const layer of TIGHTEN_ONLY_HOST_SETTINGS_LAYERS) {
411
+ const snap = read.layers[layer];
412
+ if (!snap.enabled || snap.doc === null)
413
+ continue;
414
+ const parts = tightenOnlyLayerPartsUnread(read, layer, permissionsRead);
415
+ const loaded = snap.carriers.filter((c) => c.status === 'loaded' || c.status === 'loaded-partial').map((c) => c.id);
416
+ const file = (loaded.length > 0 ? loaded : snap.carriers.map((c) => c.id)).join(', ');
417
+ const n = SETTINGS_BUILD_LAYER_PARTS_UNREAD_NOTICE({ source: layer, workspace: layerAdmissionReading(read.trust), file, parts });
418
+ if (n !== undefined)
419
+ appendOwnElement(out, notice('HS-LAYER-PARTS-UNREAD', 'session', displayUntrusted(n.line)));
420
+ }
421
+ return out;
422
+ }
370
423
  function allowAdmittedFor(read) {
371
424
  const disp = MANAGED_READING_DISPOSITIONS[read.managed];
372
- const policyDoc = read.layers.policySettings.doc;
373
- const managedOnlySwitch = disp.ruleScope === 'by-switch' && policyDoc !== null && managedOnlySwitchOn(policyDoc);
425
+ const managedOnlySwitch = managedRulesOnly(read);
374
426
  const degrade = disp.ruleScope === 'degrade';
375
427
  return (layer) => {
376
428
  if (degrade || managedOnlySwitch)
@@ -387,7 +439,7 @@ function allowAdmittedFor(read) {
387
439
  };
388
440
  }
389
441
  function badPermissionsSource() {
390
- return new HostSourceShapeError(`hostTurnSettings: sources.resolved must be 'none' or { allowGrantReading, cliArg?, session?, clientOwnedServers?, serverVersion? } — since 0.101.0 the permission rules of the settings files come from the settings the entry reads; do not pass snapshot — got something else (the value is not echoed).`);
442
+ return new HostSourceShapeError(`hostTurnSettings: sources.resolved must be 'none' or { allowGrantReading, cliArg?, session?, clientOwnedServers? } — since 0.101.0 the permission rules of the settings files come from the settings the entry reads; do not pass snapshot — got something else (the value is not echoed).`);
391
443
  }
392
444
  function readPermissionSource(src) {
393
445
  const key = 'settings.resolved';
@@ -405,12 +457,9 @@ function readPermissionSource(src) {
405
457
  let sesAsk;
406
458
  let owned;
407
459
  let ownedRaw;
408
- let serverVersion;
409
460
  try {
410
461
  if (!isRecordShape(v) || Object.hasOwn(v, 'snapshot') || !Object.hasOwn(v, 'allowGrantReading'))
411
462
  throw badPermissionsSource();
412
- const sver = ownDataOf(v, 'serverVersion');
413
- serverVersion = sver.ok && typeof sver.value === 'string' ? sver.value : undefined;
414
463
  const g = ownDataOf(v, 'allowGrantReading');
415
464
  const c = ownDataOf(v, 'cliArg');
416
465
  const se = ownDataOf(v, 'session');
@@ -444,13 +493,13 @@ function readPermissionSource(src) {
444
493
  }
445
494
  if (cliAllow === undefined || cliDeny === undefined || sesDeny === undefined || sesAsk === undefined || (owned === undefined && ownedRaw !== undefined))
446
495
  throw badPermissionsSource();
447
- return { kind: 'facts', allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned, serverVersion };
496
+ return { kind: 'facts', allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned };
448
497
  }
449
498
  function permissionsArm(facts, side, adoption, layered) {
450
499
  const key = 'settings.resolved';
451
500
  if (facts.kind === 'arm')
452
501
  return facts.arm;
453
- const { allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned, serverVersion } = facts;
502
+ const { allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned } = facts;
454
503
  if (side.kind === 'missing')
455
504
  return missingArm(key);
456
505
  const buckets = { deny: [], ask: [], allow: [] };
@@ -482,7 +531,7 @@ function permissionsArm(facts, side, adoption, layered) {
482
531
  const read = side.read;
483
532
  const disp = MANAGED_READING_DISPOSITIONS[read.managed];
484
533
  const policyDoc = read.layers.policySettings.doc;
485
- const managedOnlySwitch = disp.ruleScope === 'by-switch' && policyDoc !== null && managedOnlySwitchOn(policyDoc);
534
+ const managedOnlySwitch = managedRulesOnly(read);
486
535
  const degrade = disp.ruleScope === 'degrade';
487
536
  try {
488
537
  for (const bucket of Object.keys(buckets)) {
@@ -506,6 +555,10 @@ function permissionsArm(facts, side, adoption, layered) {
506
555
  for (const r of adoption?.extraTighten[bucket] ?? [])
507
556
  if (permissionMemberAdmitted(r.layer, read, bucket))
508
557
  add(bucket, r.rule, r.layer);
558
+ for (const l of TIGHTEN_ONLY_HOST_SETTINGS_LAYERS)
559
+ for (const bucket of tightenOnlyBucketsRead(read, l))
560
+ for (const r of permissionRulesOf(read.layers[l].doc, bucket))
561
+ add(bucket, r, l);
509
562
  }
510
563
  }
511
564
  catch (error) {
@@ -521,7 +574,7 @@ function permissionsArm(facts, side, adoption, layered) {
521
574
  let declared;
522
575
  if (projectionError === undefined) {
523
576
  try {
524
- const d = projectPermissionsDeclaration({ deny: buckets.deny, ask: buckets.ask, allow: buckets.allow, origins, cliDeny, settingsRead: 'clean', ...(owned !== undefined && owned.length > 0 ? { clientOwnedServers: owned } : {}), ...(serverVersion !== undefined ? { serverVersion } : {}) }, allowGrantReading, portsProjection, layered ? freeze({ deny: freeze([...contributions.deny]), ask: freeze([...contributions.ask]) }) : undefined);
577
+ const d = projectPermissionsDeclaration({ deny: buckets.deny, ask: buckets.ask, allow: buckets.allow, origins, cliDeny, settingsRead: 'clean', ...(owned !== undefined && owned.length > 0 ? { clientOwnedServers: owned } : {}) }, allowGrantReading, portsProjection, layered ? freeze({ deny: freeze([...contributions.deny]), ask: freeze([...contributions.ask]) }) : undefined);
525
578
  declared = d.permissions !== undefined ? { permissions: d.permissions } : undefined;
526
579
  }
527
580
  catch (error) {
@@ -584,8 +637,11 @@ function notLoadedLayers(read, plan) {
584
637
  }
585
638
  return out;
586
639
  }
640
+ const isTightenOnlyLayer = (l) => TIGHTEN_ONLY_HOST_SETTINGS_LAYERS.includes(l);
587
641
  function notLoadedSentence(n, form) {
588
642
  const layer = msg(`session.hostSettings.layer.${n.layer}`);
643
+ if (isTightenOnlyLayer(n.layer))
644
+ return renderMessage(msg(form === 'hold' ? 'session.hostSettings.layerNotLoaded.restrictions.hold' : 'session.hostSettings.layerNotLoaded.restrictions.sent', { layer }));
589
645
  return renderMessage(msg(form === 'hold' ? `session.hostSettings.layerNotLoaded.hold.${n.how}` : 'session.hostSettings.layerNotLoaded.sent.unreadable', { layer }));
590
646
  }
591
647
  function settingsSideOf(input) {
@@ -679,17 +735,19 @@ export function buildHostTurnSettings(input, judges) {
679
735
  if (notLoaded.length > 0) {
680
736
  const key = `hs1-${fnv1a(JSON.stringify(notLoaded.map((n) => n.key)))}`;
681
737
  const ask = surface === 'interactive' && !session.consented.includes(key);
682
- const layers = freeze(notLoaded.map((n) => n.layer));
683
- const cause = notLoaded.some((n) => n.how === 'unreadable') ? 'source_unreadable' : 'source_rejected';
684
- const reshape = (a) => {
738
+ const reshape = (a, feeds) => {
685
739
  const d = a.row.disposition;
686
740
  if (d === 'refuse_turn' || d === 'unwired' || a.row.cause === 'no_source')
687
741
  return a;
742
+ const mine = notLoaded.filter((n) => feeds(n.layer));
743
+ if (mine.length === 0)
744
+ return a;
745
+ const cause = mine.some((n) => n.how === 'unreadable') ? 'source_unreadable' : 'source_rejected';
688
746
  const disposition = ask ? 'hold_for_consent' : a.carried !== undefined ? 'carry_partial' : 'withhold_notice';
689
- return { ...a, row: freeze({ key: a.row.key, cause, disposition, layers }) };
747
+ return { ...a, row: freeze({ key: a.row.key, cause, disposition, layers: freeze(mine.map((n) => n.layer)) }) };
690
748
  };
691
749
  const before = armsByKey;
692
- armsByKey = [{ ...reshape(before[0]), ...(hooks.plan !== undefined ? { plan: hooks.plan } : {}) }, reshape(before[1]), before[2], before[3], before[4], before[5]];
750
+ armsByKey = [{ ...reshape(before[0], (l) => !isTightenOnlyLayer(l)), ...(hooks.plan !== undefined ? { plan: hooks.plan } : {}) }, reshape(before[1], () => true), before[2], before[3], before[4], before[5]];
693
751
  if (ask && armsByKey.slice(0, 2).some((a) => a.row.disposition === 'hold_for_consent'))
694
752
  consentKey = key;
695
753
  for (const n of notLoaded) {
@@ -709,7 +767,11 @@ export function buildHostTurnSettings(input, judges) {
709
767
  }
710
768
  const arms = armsByKey;
711
769
  const rows = freeze(arms.map((a) => a.row));
712
- const notices = freeze([...arms.flatMap((a) => a.notices), ...notLoadedNotices, ...heldNotices]);
770
+ const resolvedOut = arms[1]?.row.disposition === 'carry' || arms[1]?.row.disposition === 'carry_partial' || arms[1]?.row.disposition === 'hold_for_consent' ? resolvedCarried : undefined;
771
+ const permissionsRow = arms[1]?.row;
772
+ const permissionsRead = permissionsRow !== undefined && permissionsRow.disposition !== 'refuse_turn' && permissionsRow.disposition !== 'unwired' && permissionsRow.cause !== 'no_source';
773
+ const partsNotices = side.kind === 'read' ? tightenOnlyLayerNotices(side.read, permissionsRead) : [];
774
+ const notices = freeze([...arms.flatMap((a) => a.notices), ...notLoadedNotices, ...heldNotices, ...partsNotices]);
713
775
  const refusal = arms.find((a) => a.row.disposition === 'refuse_turn');
714
776
  const verdict = refusal !== undefined
715
777
  ? freeze({ kind: 'refuse', notice: refusal.notices[0] ?? notices.find((n) => n.plane === 'turn') ?? notice('HS-PROJECTION', 'turn') })
@@ -721,7 +783,6 @@ export function buildHostTurnSettings(input, judges) {
721
783
  return a.row.disposition === 'carry' || a.row.disposition === 'carry_partial' || a.row.disposition === 'hold_for_consent' ? a.carried : undefined;
722
784
  };
723
785
  const hooksCarried = carriedOf(0);
724
- const resolvedOut = arms[1]?.row.disposition === 'carry' || arms[1]?.row.disposition === 'carry_partial' || arms[1]?.row.disposition === 'hold_for_consent' ? resolvedCarried : undefined;
725
786
  const carried = freeze({
726
787
  ...(hooksCarried !== undefined ? { hooks: hooksCarried } : {}),
727
788
  ...(resolvedOut !== undefined ? { resolved: resolvedOut } : {}),