@sema-agent/client-core 0.104.0 → 0.105.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 (92) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +13 -5
  3. package/dist/adapt/arms.js +14 -6
  4. package/dist/adapter/activeRunSelfHeal.d.ts +1 -0
  5. package/dist/adapter/activeRunSelfHeal.js +10 -5
  6. package/dist/adapter/downstream/eventToSdkMessage.js +4 -4
  7. package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
  8. package/dist/adapter/downstream/wiringManifestView.js +25 -0
  9. package/dist/adapter/runStream.js +5 -1
  10. package/dist/cloudConfigWireCaps.js +3 -0
  11. package/dist/compensations.js +6 -6
  12. package/dist/coreValuePorts.d.ts +3 -1
  13. package/dist/coreValuePorts.js +4 -0
  14. package/dist/displayUntrusted.d.ts +4 -2
  15. package/dist/displayUntrusted.js +45 -22
  16. package/dist/engineGenerationPort.d.ts +11 -2
  17. package/dist/engineGenerationPort.js +63 -9
  18. package/dist/engineHealthFacts.d.ts +5 -0
  19. package/dist/engineHealthFacts.js +11 -0
  20. package/dist/eventLeg.d.ts +1 -1
  21. package/dist/eventLeg.js +23 -3
  22. package/dist/generated/engineFactTables.d.ts +1 -0
  23. package/dist/generated/engineFactTables.js +1 -0
  24. package/dist/generated/sdkFactTables.d.ts +2 -0
  25. package/dist/generated/sdkFactTables.js +9 -0
  26. package/dist/headlessReconnectWire.js +31 -4
  27. package/dist/hitl/askGateWire.js +26 -2
  28. package/dist/hitl/frameRouter.d.ts +4 -0
  29. package/dist/hitl/frameRouter.js +14 -0
  30. package/dist/hitl/gateIdentity.d.ts +1 -0
  31. package/dist/hitl/gateIdentity.js +3 -0
  32. package/dist/hitl/livePendingAsk.d.ts +2 -1
  33. package/dist/hitl/livePendingAsk.js +7 -1
  34. package/dist/hitl/planReviewCard.d.ts +21 -0
  35. package/dist/hitl/planReviewCard.js +229 -0
  36. package/dist/hitl/planReviewWire.d.ts +38 -13
  37. package/dist/hitl/planReviewWire.js +355 -133
  38. package/dist/hitl/toolApprovalWire.d.ts +2 -2
  39. package/dist/hitl/toolApprovalWire.js +53 -32
  40. package/dist/hostSettingsSource.d.ts +1 -1
  41. package/dist/hostSettingsSource.js +8 -4
  42. package/dist/hostTurnSettings.d.ts +1 -1
  43. package/dist/hostTurnSettings.js +29 -10
  44. package/dist/i18n/messages.en.d.ts +50 -0
  45. package/dist/i18n/messages.en.js +50 -0
  46. package/dist/i18n/messages.zh.js +51 -0
  47. package/dist/index.d.ts +15 -3
  48. package/dist/index.js +9 -1
  49. package/dist/mcpTransportKindsCapability.d.ts +18 -0
  50. package/dist/mcpTransportKindsCapability.js +75 -0
  51. package/dist/mcpWireCaps.d.ts +4 -1
  52. package/dist/mcpWireCaps.js +24 -13
  53. package/dist/notifications.d.ts +9 -0
  54. package/dist/notifications.js +11 -2
  55. package/dist/ownKey.d.ts +4 -0
  56. package/dist/ownKey.js +8 -0
  57. package/dist/permissionModeWriteCapability.d.ts +13 -0
  58. package/dist/permissionModeWriteCapability.js +35 -0
  59. package/dist/permissionsAllowGrantCapability.d.ts +3 -0
  60. package/dist/permissionsAllowGrantCapability.js +26 -3
  61. package/dist/permissionsDeclaration.d.ts +23 -1
  62. package/dist/permissionsDeclaration.js +60 -30
  63. package/dist/planReviewCardFacts.d.ts +116 -0
  64. package/dist/planReviewCardFacts.js +469 -0
  65. package/dist/reattachWindow.d.ts +8 -1
  66. package/dist/reattachWindow.js +44 -12
  67. package/dist/runFollow.d.ts +30 -0
  68. package/dist/runFollow.js +117 -0
  69. package/dist/runLedgerSnapshot.d.ts +20 -0
  70. package/dist/runLedgerSnapshot.js +272 -0
  71. package/dist/runPermissionModeSwitch.d.ts +57 -0
  72. package/dist/runPermissionModeSwitch.js +135 -0
  73. package/dist/runReattachPolicy.d.ts +13 -5
  74. package/dist/runReattachPolicy.js +58 -23
  75. package/dist/seam.d.ts +3 -0
  76. package/dist/seam.js +1 -1
  77. package/dist/seatContract.js +1 -0
  78. package/dist/sessionMemoryErase.d.ts +1 -1
  79. package/dist/sessionMemoryErase.js +5 -0
  80. package/dist/sessionMemoryStatus.d.ts +27 -1
  81. package/dist/sessionMemoryStatus.js +117 -0
  82. package/dist/sseIdleTriage.d.ts +3 -1
  83. package/dist/sseIdleTriage.js +19 -4
  84. package/dist/steering.d.ts +1 -0
  85. package/dist/steering.js +34 -26
  86. package/dist/subagent/engineSubagentTail.js +2 -0
  87. package/dist/subagentContentStore.d.ts +3 -0
  88. package/dist/subagentContentStore.js +10 -1
  89. package/dist/turnFailurePresentation.d.ts +5 -2
  90. package/dist/turnFailurePresentation.js +12 -4
  91. package/docs/INTEGRATION-CLIENTS.md +587 -10
  92. package/package.json +2 -2
@@ -10,7 +10,7 @@ import { type ApprovalMandateWord } from '../gateVocabulary.js';
10
10
  import type { ToolPermissionRequest } from '../seatContract.js';
11
11
  import { type ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
12
12
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS } from '@sema-agent/sdk';
13
- import type { ApprovalDecidedBy, ApprovalCardDelegation, ToolApprovalDecision as SdkToolApprovalDecision, ToolApprovalRespondAck, ToolApprovalRespondReplayed, PersistedRuleAnchor, RuleOfferMatch, RuleOfferBatchMember, RuleOfferUncoveredDetail, RuleOfferUncoveredReason, AskOrigin, RuleOffersAbsence, DenialLimitKind, DenialLimitFallback, ToolApprovalFrame, RuleOffer as SdkRuleOffer } from '@sema-agent/sdk';
13
+ import type { ApprovalDecidedBy, ApprovalCardDelegation, ToolApprovalDecision as SdkToolApprovalDecision, ToolApprovalRespondAck, ToolApprovalRespondReplayed, PersistedRuleAnchor, RuleOfferMatch, RuleOfferBatchMember, RuleOfferUncoveredDetail, RuleOfferUncoveredReason, AskOrigin, RuleOffersAbsence, DenialLimitKind, DenialLimitFallback, ToolApprovalFrame, ApprovalRequestFrame, RuleOffer as SdkRuleOffer } from '@sema-agent/sdk';
14
14
  export declare function toolNameIsFsWrite(name: string): boolean;
15
15
  export declare function toolNameIsShellExec(name: string): boolean;
16
16
  export interface ToolApprovalGateLike {
@@ -293,7 +293,7 @@ export interface ToolApprovalFrameLaneOpts {
293
293
  windowIsCurrent?: boolean;
294
294
  argsUnavailable?: true;
295
295
  }
296
- export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: {
296
+ export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame | ApprovalRequestFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: {
297
297
  readonly taskId?: string;
298
298
  readonly sessionId?: string;
299
299
  }): Promise<ApprovalResolution>;
@@ -31,7 +31,7 @@ import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
31
31
  import { isPlainRecord } from '../jsonFaithful.js';
32
32
  import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
33
33
  import { readDecideReceipt } from '../decideReceipt.js';
34
- import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, } from '@sema-agent/sdk';
34
+ import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, isApprovalRequestFrameV1, } from '@sema-agent/sdk';
35
35
  export function toolNameIsFsWrite(name) {
36
36
  const n = name.replace(/[\s_-]+/g, '').toLowerCase();
37
37
  return (n === 'write' || n === 'filewrite' || n === 'edit' || n === 'fileedit' || n === 'multiedit' || n === 'notebookedit');
@@ -470,7 +470,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
470
470
  });
471
471
  }
472
472
  function parkRowHolderIdentity(row, gatedCallId) {
473
- const askSig = carrierAskSig(row);
473
+ const askSig = carrierAskSig(row, row);
474
474
  return {
475
475
  ...(typeof row.taskId === 'string' && row.taskId !== '' ? { taskId: row.taskId } : {}),
476
476
  ...(gatedCallId !== undefined && gatedCallId !== '' ? { toolCallId: gatedCallId } : {}),
@@ -1210,16 +1210,18 @@ function serverMayReadCallLaw(carrier) {
1210
1210
  const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
1211
1211
  frame: Object.freeze([
1212
1212
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1213
- ['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
1213
+ ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
1214
1214
  ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1215
1215
  ]),
1216
+ live: Object.freeze([['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin']]),
1216
1217
  row: Object.freeze([
1217
1218
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1218
1219
  ['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1219
1220
  ['askClass', 'askClass'], ['classes', 'classes'],
1220
1221
  ]),
1221
1222
  v1: Object.freeze([
1222
- ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1223
+ ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1224
+ ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1223
1225
  ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1224
1226
  ]),
1225
1227
  generic: Object.freeze([['ruleOffersAbsence', 'ruleOffersAbsence'], ['askClass', 'askClass'], ['classes', 'classes']]),
@@ -1235,12 +1237,18 @@ function riskSubtreeUnreadable(card) {
1235
1237
  return true;
1236
1238
  }
1237
1239
  }
1238
- function withUnreadableEvidence(carrier, leg, req, reported = []) {
1240
+ function withUnreadableEvidence(carrier, leg, req, reported = [], live) {
1239
1241
  const keys = new Set(reported);
1240
1242
  for (const [from, to] of UNREADABLE_EVIDENCE_KEYS[leg]) {
1241
1243
  if (slotPresenceOf(carrier, from) !== 'absent' && !Object.hasOwn(req, to))
1242
1244
  keys.add(from);
1243
1245
  }
1246
+ if (live !== undefined) {
1247
+ for (const [from, to] of UNREADABLE_EVIDENCE_KEYS.live) {
1248
+ if (slotPresenceOf(live, from) !== 'absent' && !Object.hasOwn(req, to))
1249
+ keys.add(from);
1250
+ }
1251
+ }
1244
1252
  if (leg === 'row') {
1245
1253
  const rd = ownDataValue(carrier, 'riskDescriptor');
1246
1254
  if (typeof rd === 'object' && rd !== null && slotPresenceOf(rd, 'allowRuleShadowed') !== 'absent' && !Object.hasOwn(req, 'allowRuleShadow'))
@@ -1310,9 +1318,9 @@ function askSignatureOf(req) {
1310
1318
  function askSigOf(classes, origin, mandate) {
1311
1319
  return JSON.stringify([classes !== undefined ? [...classes] : null, origin ?? null, mandate ?? null]);
1312
1320
  }
1313
- function carrierAskSig(v) {
1321
+ function carrierAskSig(v, live) {
1314
1322
  try {
1315
- return askSigOf(readAskClassPair(v)?.classes, readAskOrigin(v), readApprovalMandate(v));
1323
+ return askSigOf(readAskClassPair(v)?.classes, live === undefined ? undefined : readAskOrigin(live), live === undefined ? undefined : readApprovalMandate(live));
1316
1324
  }
1317
1325
  catch {
1318
1326
  return undefined;
@@ -1623,9 +1631,10 @@ function v1Bit(stamp) {
1623
1631
  const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1624
1632
  'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1625
1633
  'Edits are not accepted on this card: there is no original input to edit';
1626
- function cardFactsOf(carrier, leg, opts) {
1634
+ function cardFactsOf(carrier, leg, opts, liveFacts) {
1627
1635
  const c = carrier;
1628
1636
  const full = leg !== 'generic';
1637
+ const live = leg === 'frame' ? carrier : leg === 'v1' ? liveFacts : undefined;
1629
1638
  const asFrame = carrier;
1630
1639
  const fromSubagent = full
1631
1640
  ? (() => {
@@ -1641,7 +1650,7 @@ function cardFactsOf(carrier, leg, opts) {
1641
1650
  try {
1642
1651
  let stream;
1643
1652
  let blind = false;
1644
- if (leg === 'frame' && typeof opts === 'object' && opts !== null) {
1653
+ if (typeof opts === 'object' && opts !== null) {
1645
1654
  stream = opts.streamArgs;
1646
1655
  blind = opts.argsUnavailable === true;
1647
1656
  }
@@ -1714,9 +1723,9 @@ function cardFactsOf(carrier, leg, opts) {
1714
1723
  if (isWireRecordCarrier(pc))
1715
1724
  out.probeCause = pc;
1716
1725
  });
1717
- if (leg === 'frame') {
1726
+ if (live !== undefined) {
1718
1727
  v1Bit(() => {
1719
- const r = readProbeReason(carrier);
1728
+ const r = readProbeReason(live);
1720
1729
  if (r !== undefined)
1721
1730
  out.probeReason = r;
1722
1731
  });
@@ -1737,18 +1746,18 @@ function cardFactsOf(carrier, leg, opts) {
1737
1746
  catch {
1738
1747
  realApprovalUnreadable = true;
1739
1748
  }
1740
- if (leg === 'frame') {
1749
+ if (live !== undefined) {
1741
1750
  v1Bit(() => {
1742
- if (hasOwnStrictTrue(carrier, 'mandated'))
1751
+ if (hasOwnStrictTrue(live, 'mandated'))
1743
1752
  out.mandated = true;
1744
1753
  });
1745
1754
  v1Bit(() => {
1746
- const w = readApprovalMandate(carrier);
1755
+ const w = readApprovalMandate(live);
1747
1756
  if (w !== undefined)
1748
1757
  out.mandate = w;
1749
1758
  });
1750
1759
  v1Bit(() => {
1751
- const rc = readReadRootCandidate(c.readRootCandidate);
1760
+ const rc = readReadRootCandidate(live.readRootCandidate);
1752
1761
  if (rc !== undefined)
1753
1762
  out.readRootCandidate = rc;
1754
1763
  });
@@ -1768,9 +1777,9 @@ function cardFactsOf(carrier, leg, opts) {
1768
1777
  if (dlf !== undefined)
1769
1778
  out.denialLimitFallback = dlf;
1770
1779
  });
1771
- if (leg === 'frame') {
1780
+ if (live !== undefined) {
1772
1781
  v1Bit(() => {
1773
- const o = readAskOrigin(carrier);
1782
+ const o = readAskOrigin(live);
1774
1783
  if (o !== undefined)
1775
1784
  out.origin = o;
1776
1785
  });
@@ -1789,14 +1798,14 @@ function cardFactsOf(carrier, leg, opts) {
1789
1798
  out.classes = pair.classes;
1790
1799
  }
1791
1800
  });
1792
- if (leg === 'frame') {
1801
+ if (live !== undefined) {
1793
1802
  v1Bit(() => {
1794
- const g = readApprovalBoundGrants(carrier);
1803
+ const g = readApprovalBoundGrants(live);
1795
1804
  if (g !== undefined)
1796
1805
  out.boundGrants = g;
1797
1806
  });
1798
1807
  v1Bit(() => {
1799
- const w = readApprovalClassifierNoRuling(carrier);
1808
+ const w = readApprovalClassifierNoRuling(live);
1800
1809
  if (w !== undefined)
1801
1810
  out.classifierNoRuling = w;
1802
1811
  });
@@ -1811,7 +1820,7 @@ function cardFactsOf(carrier, leg, opts) {
1811
1820
  if (leg === 'frame')
1812
1821
  out.allowSessionCarried = true;
1813
1822
  out.grantBinding = readApprovalGrantBinding(carrier);
1814
- return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : []);
1823
+ return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : [], live);
1815
1824
  }
1816
1825
  export function approvalCardFactsOfV1(card) {
1817
1826
  if (typeof card !== 'object' || card === null)
@@ -1832,9 +1841,19 @@ const MAX_RESPOND_NOTE_CHARS = 2048;
1832
1841
  export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
1833
1842
  const receivedAtMs = Date.now();
1834
1843
  const approvalId = frame.approvalId;
1835
- const entry = { frame, approvalId, identity: liveCardIdentityOf(approvalId, frame, run), respond, streamArgs, signal, lane, receivedAtMs };
1844
+ const carriers = openingFrameCarriersOf(frame);
1845
+ const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, frame, carriers, run), respond, streamArgs, signal, lane, receivedAtMs };
1836
1846
  return enterLiveCard(entry);
1837
1847
  }
1848
+ function openingFrameCarriersOf(frame) {
1849
+ if (frame.type !== 'approval_request')
1850
+ return { body: frame, live: frame, leg: 'frame' };
1851
+ const card = frame.card;
1852
+ const pinned = Object.create(frame, { card: { value: card, enumerable: true } });
1853
+ if (isApprovalRequestFrameV1(pinned))
1854
+ return { body: pinned.card, live: frame, leg: 'v1' };
1855
+ return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic' };
1856
+ }
1838
1857
  function enterLiveCard(e) {
1839
1858
  const { approvalId, signal } = e;
1840
1859
  const held = liveCardHoldOf(liveFrameCallKey(approvalId));
@@ -1848,7 +1867,7 @@ function enterLiveCard(e) {
1848
1867
  return joinOtherLaneHolder(e, other);
1849
1868
  return holdLiveCard(e.identity, signal, async (hold) => {
1850
1869
  const slot = {};
1851
- const out = await presentToolApprovalFrameAndRespond(e.frame, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
1870
+ const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
1852
1871
  noteCallOutcome(slot, out);
1853
1872
  return out;
1854
1873
  });
@@ -1863,15 +1882,16 @@ async function joinOtherLaneHolder(e, other) {
1863
1882
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
1864
1883
  return enterLiveCard(e);
1865
1884
  }
1866
- function liveCardIdentityOf(approvalId, frame, run) {
1867
- const toolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1868
- const fromSubagent = isFromSubagent(frame);
1885
+ function liveCardIdentityOf(approvalId, frame, carriers, run) {
1886
+ const body = carriers.body;
1887
+ const toolCallId = typeof body.toolCallId === 'string' && body.toolCallId !== '' ? body.toolCallId : undefined;
1888
+ const fromSubagent = isFromSubagent(carriers.body);
1869
1889
  const bound = fromSubagent ? undefined : frameBindingOf(frame);
1870
1890
  const source = bound ?? run;
1871
- const ownRun = fromSubagent ? frame.sourceTaskId : source?.taskId;
1891
+ const ownRun = fromSubagent ? body.sourceTaskId : source?.taskId;
1872
1892
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1873
1893
  const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
1874
- const askSig = carrierAskSig(frame);
1894
+ const askSig = carrierAskSig(carriers.body, carriers.live);
1875
1895
  return {
1876
1896
  approvalId,
1877
1897
  ...(toolCallId !== undefined ? { toolCallId } : {}),
@@ -1926,10 +1946,11 @@ function readApprovalCardClaimSignal(v) {
1926
1946
  export function claimApprovalCard(identity, options) {
1927
1947
  return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
1928
1948
  }
1929
- async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
1930
- const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1949
+ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
1950
+ const bodyToolCallId = carriers.body.toolCallId;
1951
+ const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
1931
1952
  const decidedByReading = decidedByReadingSnapshotOf(lane);
1932
- const facts = cardFactsOf(frame, 'frame', { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) });
1953
+ const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live);
1933
1954
  const argsNotRealInput = facts.argsUnavailable === true;
1934
1955
  const ruleOffers = facts.ruleOffers;
1935
1956
  if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
@@ -1939,7 +1960,7 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1939
1960
  ...facts,
1940
1961
  callKey: liveFrameCallKey(approvalId),
1941
1962
  signal: hold.cardSignal,
1942
- ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs }),
1963
+ ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs, ...(carriers.leg === 'generic' ? { unknownShape: true } : {}) }),
1943
1964
  });
1944
1965
  if (hold.superseded !== undefined) {
1945
1966
  hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
@@ -121,7 +121,7 @@ export interface HostSettingsAdoption {
121
121
  };
122
122
  readonly overflow?: true;
123
123
  }
124
- export declare function adoptHostSettingsRead(read: HostSettingsRead, session: HostSettingsSession, allowAdmitted?: (layer: AdoptedLayer) => boolean): HostSettingsAdoption;
124
+ export declare function adoptHostSettingsRead(read: HostSettingsRead, session: HostSettingsSession, allowAdmitted?: (layer: AdoptedLayer) => boolean, layeredRules?: boolean): HostSettingsAdoption;
125
125
  export type ManagedCommitVerdict = 'proceed' | 'refuse';
126
126
  export type ManagedRuleScopeVerdict = 'all-sources' | 'by-switch' | 'degrade';
127
127
  export type ManagedHooksRestrictionVerdict = 'use-read' | 'held-restrictions' | 'withhold';
@@ -524,7 +524,7 @@ function overflowOf(read, session) {
524
524
  return Object.freeze({ read, session: remintSameContent(session), trusted: false, clean: false, extraTighten: NO_EXTRA, allowByLayer: emptyByLayer(), overflow: true });
525
525
  }
526
526
  const carrierKeysOf = (read, layer) => read.layers[layer].carriers.map((c) => c.id);
527
- export function adoptHostSettingsRead(read, session, allowAdmitted = () => true) {
527
+ export function adoptHostSettingsRead(read, session, allowAdmitted = () => true, layeredRules = false) {
528
528
  const B = session.baseline;
529
529
  const stackIssues = ADOPTED_LAYERS.flatMap((l) => read.layers[l].issues.map((x) => `${l}|${x}`));
530
530
  const loadedNow = ADOPTED_LAYERS.flatMap((l) => read.layers[l].malformed ? [] : read.layers[l].carriers.filter((c) => LOADED.has(c.status) || (c.status === 'empty' && c.emptyKind === 'empty-object')).map((c) => `${l}|${c.id}`));
@@ -627,9 +627,13 @@ export function adoptHostSettingsRead(read, session, allowAdmitted = () => true)
627
627
  effectiveLayers[l] = l === 'policySettings' ? snap : Object.freeze({ ...snap, doc: base.docs[l] });
628
628
  }
629
629
  const effective = Object.freeze({ ...read, layers: Object.freeze(effectiveLayers), current: read, debug: Object.freeze([]) });
630
- const sentNow = (bucket) => new Set([...ADOPTED_LAYERS.flatMap((l) => permissionRulesOf(base.docs[l], bucket)), ...extraTighten[bucket].map((r) => r.rule)]);
631
- const readNow = (bucket) => new Set(ADOPTED_LAYERS.flatMap((l) => permissionRulesOf(read.layers[l].doc, bucket)));
632
- const drop = (bucket) => Object.freeze([...sentNow(bucket)].filter((r) => !readNow(bucket).has(r)).sort());
630
+ const idOf = (layer, rule) => (layeredRules ? JSON.stringify([layer, rule]) : rule);
631
+ const sentNow = (bucket) => [...ADOPTED_LAYERS.flatMap((l) => permissionRulesOf(base.docs[l], bucket).map((rule) => ({ layer: l, rule }))), ...extraTighten[bucket]];
632
+ const readNow = (bucket) => new Set(ADOPTED_LAYERS.flatMap((l) => permissionRulesOf(read.layers[l].doc, bucket).map((rule) => idOf(l, rule))));
633
+ const drop = (bucket) => {
634
+ const now = readNow(bucket);
635
+ return Object.freeze([...new Set(sentNow(bucket).filter((r) => !now.has(idOf(r.layer, r.rule))).map((r) => r.rule))].sort());
636
+ };
633
637
  const wouldDrop = Object.freeze({ deny: drop('deny'), ask: drop('ask') });
634
638
  const acceptable = !stackIssues.some((x) => !base.issues.includes(x)) && !anyMalformed && !ADOPTED_LAYERS.some((l) => read.layers[l].carriers.some((c) => c.status === 'unreadable' || c.status === 'not-loaded'));
635
639
  const s = mintHostSettingsSession(parentVersionOf(session), session.consented, baseline);
@@ -8,7 +8,7 @@ import { type HostSettingsSession, type HostSettingsSource } from './hostSetting
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';
11
- import type { PermissionsAllowGrantReading } from './permissionsAllowGrantCapability.js';
11
+ import { type PermissionsAllowGrantReading } from './permissionsAllowGrantCapability.js';
12
12
  export declare const HOST_SUPPLIED_KEYS: readonly ["settings.hooks", "settings.resolved", "settings.webSearch", "settings.outputStyle", "memory", "fileHistory"];
13
13
  export type HostSuppliedKey = (typeof HOST_SUPPLIED_KEYS)[number];
14
14
  export declare const HOST_SETTING_CAUSES: readonly ["carried", "entries_dropped", "source_rejected", "projection_failed", "source_unreadable", "empty", "no_source", "host_port_missing", "value_malformed", "held_previous", "policy_withheld"];
@@ -10,6 +10,7 @@ import { fileHistoryTightenVerdict, fileHistoryUnjudgedRefusal } from './fileHis
10
10
  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
+ import { permissionRuleLayersOn } from './permissionsAllowGrantCapability.js';
13
14
  import { layerAdmitted } from './settingsLayerAdmission.js';
14
15
  import { appendOwnElement, ownDataField } from './ownKey.js';
15
16
  export const HOST_SUPPLIED_KEYS = Object.freeze(['settings.hooks', 'settings.resolved', 'settings.webSearch', 'settings.outputStyle', 'memory', 'fileHistory']);
@@ -388,15 +389,15 @@ function allowAdmittedFor(read) {
388
389
  function badPermissionsSource() {
389
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).`);
390
391
  }
391
- function permissionsArm(src, side, adoption) {
392
+ function readPermissionSource(src) {
392
393
  const key = 'settings.resolved';
393
394
  if (src.kind === 'missing')
394
- return missingArm(key);
395
+ return { kind: 'arm', arm: missingArm(key) };
395
396
  if (src.kind === 'threw')
396
- return unreadableArm(key);
397
+ return { kind: 'arm', arm: unreadableArm(key) };
397
398
  const v = src.value;
398
399
  if (v === 'none')
399
- return noSourceArm(key);
400
+ return { kind: 'arm', arm: noSourceArm(key) };
400
401
  let allowGrantReading;
401
402
  let cliAllow;
402
403
  let cliDeny;
@@ -415,7 +416,7 @@ function permissionsArm(src, side, adoption) {
415
416
  const se = ownDataOf(v, 'session');
416
417
  const o = ownDataOf(v, 'clientOwnedServers');
417
418
  if (!g.ok || !c.ok || !se.ok || !o.ok)
418
- return unreadableArm(key);
419
+ return { kind: 'arm', arm: unreadableArm(key) };
419
420
  allowGrantReading = g.value;
420
421
  const cv = c.value;
421
422
  const sv = se.value;
@@ -428,7 +429,7 @@ function permissionsArm(src, side, adoption) {
428
429
  const sd = sv === undefined ? { ok: true, value: undefined } : ownDataOf(sv, 'deny');
429
430
  const sa = sv === undefined ? { ok: true, value: undefined } : ownDataOf(sv, 'ask');
430
431
  if (!ca.ok || !cd.ok || !sd.ok || !sa.ok)
431
- return unreadableArm(key);
432
+ return { kind: 'arm', arm: unreadableArm(key) };
432
433
  cliAllow = stringListOf(ca.value);
433
434
  cliDeny = stringListOf(cd.value);
434
435
  sesDeny = stringListOf(sd.value);
@@ -439,16 +440,32 @@ function permissionsArm(src, side, adoption) {
439
440
  catch (e) {
440
441
  if (e instanceof HostSourceShapeError)
441
442
  throw e;
442
- return unreadableArm(key);
443
+ return { kind: 'arm', arm: unreadableArm(key) };
443
444
  }
444
445
  if (cliAllow === undefined || cliDeny === undefined || sesDeny === undefined || sesAsk === undefined || (owned === undefined && ownedRaw !== undefined))
445
446
  throw badPermissionsSource();
447
+ return { kind: 'facts', allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned, serverVersion };
448
+ }
449
+ function permissionsArm(facts, side, adoption, layered) {
450
+ const key = 'settings.resolved';
451
+ if (facts.kind === 'arm')
452
+ return facts.arm;
453
+ const { allowGrantReading, cliAllow, cliDeny, sesDeny, sesAsk, owned, serverVersion } = facts;
446
454
  if (side.kind === 'missing')
447
455
  return missingArm(key);
448
456
  const buckets = { deny: [], ask: [], allow: [] };
449
457
  const origins = { deny: new Map(), ask: new Map(), allow: new Map() };
450
458
  const rank = (o) => WIRE_RULE_TRUST_ORDER.indexOf(o);
459
+ const contributions = { deny: [], ask: [] };
460
+ const contributed = { deny: new Set(), ask: new Set() };
451
461
  const add = (bucket, rule, origin) => {
462
+ if (bucket !== 'allow') {
463
+ const k = JSON.stringify([origin, rule]);
464
+ if (!contributed[bucket].has(k)) {
465
+ contributed[bucket].add(k);
466
+ appendOwnElement(contributions[bucket], freeze({ rule, origin }));
467
+ }
468
+ }
452
469
  const prev = origins[bucket].get(rule);
453
470
  if (prev === undefined) {
454
471
  appendOwnElement(buckets[bucket], rule);
@@ -504,7 +521,7 @@ function permissionsArm(src, side, adoption) {
504
521
  let declared;
505
522
  if (projectionError === undefined) {
506
523
  try {
507
- 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);
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);
508
525
  declared = d.permissions !== undefined ? { permissions: d.permissions } : undefined;
509
526
  }
510
527
  catch (error) {
@@ -615,10 +632,12 @@ export function buildHostTurnSettings(input, judges) {
615
632
  if (rawSide.kind === 'read')
616
633
  for (const line of rawSide.read.debug)
617
634
  hostLog('debug', line);
618
- const adoption = rawSide.kind === 'read' ? adoptHostSettingsRead(rawSide.read, session, allowAdmittedFor(rawSide.read)) : undefined;
635
+ const permissionSource = readPermissionSource(read('resolved'));
636
+ const layeredRules = permissionSource.kind === 'facts' && permissionRuleLayersOn(permissionSource.allowGrantReading);
637
+ const adoption = rawSide.kind === 'read' ? adoptHostSettingsRead(rawSide.read, session, allowAdmittedFor(rawSide.read), layeredRules) : undefined;
619
638
  const side = adoption !== undefined ? { kind: 'read', read: adoption.read } : rawSide;
620
639
  const nextSession = adoption?.session ?? remintSameContent(session);
621
- const resolvedArm = permissionsArm(read('resolved'), side, adoption);
640
+ const resolvedArm = permissionsArm(permissionSource, side, adoption, layeredRules);
622
641
  let resolvedCarried = resolvedArm.carried;
623
642
  let resolvedError;
624
643
  if (resolvedCarried !== undefined) {
@@ -187,6 +187,8 @@ declare const EN: {
187
187
  'config.mcp.nameReserved': string;
188
188
  'config.mcp.identityHeadersBothSet': string;
189
189
  'config.mcp.transportUnreadable': string;
190
+ 'config.mcp.transportNotAdvertised': string;
191
+ 'config.mcp.transportsNone': string;
190
192
  'config.mcp.transportInvalid': string;
191
193
  'config.mcp.transportDegraded': string;
192
194
  'config.mcp.toolAxesInvalid': string;
@@ -547,6 +549,13 @@ declare const EN: {
547
549
  'session.fileHistoryRefusal.notSaid': string;
548
550
  'session.fileHistoryRefusal.unrecognized': string;
549
551
  'session.fileHistoryRefusal.unreadable': string;
552
+ 'session.memoryErase.personalStoreUnjudged': string;
553
+ 'session.memorySync.parked.one': string;
554
+ 'session.memorySync.parked.other': string;
555
+ 'session.memorySync.parkedUnnamed.one': string;
556
+ 'session.memorySync.parkedUnnamed.other': string;
557
+ 'session.memorySync.wayOut.deleteCursor': string;
558
+ 'session.memorySync.wayOut.unrecognized': string;
550
559
  'session.hostSettings.memorySwitchMalformed': string;
551
560
  'session.hostSettings.fileHistoryRestoreConflict': string;
552
561
  'session.hostSettings.projectionUnchecked': string;
@@ -670,6 +679,18 @@ declare const EN: {
670
679
  'session.steerRefusal.advice': string;
671
680
  'session.steerRefusal.hostQueue.queued': string;
672
681
  'session.steerRefusal.hostQueue.dropped': string;
682
+ 'session.permissionModeSwitch.applied': string;
683
+ 'session.permissionModeSwitch.folded': string;
684
+ 'session.permissionModeSwitch.reason.entryFloor': string;
685
+ 'session.permissionModeSwitch.reason.disableAutoMode': string;
686
+ 'session.permissionModeSwitch.reason.engine': string;
687
+ 'session.permissionModeSwitch.notPersisted': string;
688
+ 'session.permissionModeSwitch.notRunning': string;
689
+ 'session.permissionModeSwitch.notFound': string;
690
+ 'session.permissionModeSwitch.refused': string;
691
+ 'session.permissionModeSwitch.refusedWithCode': string;
692
+ 'session.permissionModeSwitch.unconfirmed': string;
693
+ 'session.permissionModeSwitch.unavailable': string;
673
694
  'notice.capability.subject.userInvokedSkills': string;
674
695
  'notice.capability.subject.skillToolLists': string;
675
696
  'notice.capability.subject.permissionRulesWrite': string;
@@ -1053,6 +1074,35 @@ declare const EN: {
1053
1074
  'config.mods.refusal.by': string;
1054
1075
  'config.mods.rewrite.resultInPlace': string;
1055
1076
  'config.mods.rewrite.inputBy': string;
1077
+ 'follow.notFollowed.headRun': string;
1078
+ 'follow.notFollowed.headTurn': string;
1079
+ 'follow.notFollowed.reason.exhaustedDraining': string;
1080
+ 'follow.notFollowed.reason.exhaustedConnectionLost': string;
1081
+ 'follow.notFollowed.reason.exhaustedStreamCap': string;
1082
+ 'follow.notFollowed.reason.exhausted': string;
1083
+ 'follow.notFollowed.reason.badInput': string;
1084
+ 'follow.notFollowed.reason.noRunId': string;
1085
+ 'follow.notFollowed.reason.runUnknown': string;
1086
+ 'follow.notFollowed.reason.unspecified': string;
1087
+ 'follow.notFollowed.endingUnknown': string;
1088
+ 'follow.notFollowed.exit.reattach': string;
1089
+ 'follow.notFollowed.exit.checkRuns': string;
1090
+ 'follow.notFollowed.exit.runUnknown': string;
1091
+ 'follow.notFollowed.idleTear': string;
1092
+ 'follow.notFollowed.idleGrace': string;
1093
+ 'approval.planReview.note.empty': string;
1094
+ 'approval.planReview.note.marked': string;
1095
+ 'approval.planReview.note.tooLarge': string;
1096
+ 'approval.planReview.note.notCarried': string;
1097
+ 'approval.planReview.note.unknown': string;
1098
+ 'approval.planReview.askAgain.shown': string;
1099
+ 'approval.planReview.askAgain.marked': string;
1100
+ 'approval.planReview.askAgain.tooLarge': string;
1101
+ 'approval.planReview.askAgain.notCarried': string;
1102
+ 'approval.planReview.askAgain.unknown': string;
1103
+ 'approval.planReview.askAgain.withNote': string;
1104
+ 'approval.planReview.option.askAgain.label': string;
1105
+ 'approval.planReview.option.askAgain.description': string;
1056
1106
  };
1057
1107
  export declare const EN_MESSAGES: Readonly<typeof EN>;
1058
1108
  export type MessageKey = keyof typeof EN;
@@ -187,6 +187,8 @@ const EN = {
187
187
  'config.mcp.nameReserved': 'MCP server name {name}{where} cannot be used, so the server was not started: names starting with "remote-" are kept for the MCP servers a connected device offers. Rename the server.',
188
188
  'config.mcp.identityHeadersBothSet': 'MCP server {name}{where} was not started: it sets both principalHeader and assertionHeader, which name the same identity header — keep only one of the two. The other MCP servers are unaffected.',
189
189
  'config.mcp.transportUnreadable': 'could not be read (a getter or proxy trap on the declaration threw)',
190
+ 'config.mcp.transportNotAdvertised': 'MCP server {name}{where} was not started: it uses the {kind} transport, which the engine in use says it does not support (it supports: {supported}). The other MCP servers are unaffected.',
191
+ 'config.mcp.transportsNone': 'none',
190
192
  'config.mcp.transportInvalid': 'MCP server {name}{where} was not started: its transport setting cannot be used — {path} {problem}. Fix that setting for this server (or remove the server); the other MCP servers are unaffected.',
191
193
  'config.mcp.transportDegraded': 'MCP server {name}{where} was started without part of its transport settings, which cannot be used as written: {lost}. The rest of its settings are applied; fix the settings named above to get them applied too.',
192
194
  'config.mcp.toolAxesInvalid': 'MCP server {name}{where} was not started: its toolAxes setting is not valid — {problem}. Fix the toolAxes entry for this server (or remove it); the other MCP servers are unaffected.',
@@ -547,6 +549,13 @@ const EN = {
547
549
  'session.fileHistoryRefusal.notSaid': 'Nothing was sent: file checkpointing is turned off in your settings, but this engine did not report whether it can stop recording file history for a session, so sending now could record it anyway. Trying again will not change that while this engine reports the same; to send and let file history be recorded this time, turn file checkpointing back on in your settings.',
548
550
  'session.fileHistoryRefusal.unrecognized': 'Nothing was sent: file checkpointing is turned off in your settings, but this engine reported a file-history capture mode this client does not recognize, so whether it can stop recording file history for a session is unknown and sending now could record it anyway. Trying again will not change that while this engine reports the same; to send and let file history be recorded this time, turn file checkpointing back on in your settings.',
549
551
  'session.fileHistoryRefusal.unreadable': "Nothing was sent: file checkpointing is turned off in your settings, but this engine's report on whether it can stop recording file history for a session could not be read by this client, so sending now could record it anyway. Trying again will not change that while this engine reports the same; to send and let file history be recorded this time, turn file checkpointing back on in your settings.",
552
+ 'session.memoryErase.personalStoreUnjudged': "Nothing was erased: part of this session's memory is kept in a personal memory store that this deployment cannot erase yet, so the erase was declined before anything changed.",
553
+ 'session.memorySync.parked.one': 'Memory sync is paused on this deployment for the "{scope}" layer: its sync cursor could not be used. {wayOut}',
554
+ 'session.memorySync.parked.other': 'Memory sync is paused on this deployment for {count} layers ({scopes}): their sync cursors could not be used. {wayOut}',
555
+ 'session.memorySync.parkedUnnamed.one': 'Memory sync is paused on this deployment for 1 layer: its sync cursor could not be used. {wayOut}',
556
+ 'session.memorySync.parkedUnnamed.other': 'Memory sync is paused on this deployment for {count} layers: their sync cursors could not be used. {wayOut}',
557
+ 'session.memorySync.wayOut.deleteCursor': 'Syncing resumes once an operator deletes the sync cursor of each paused layer on the server; entries deleted on only one side since the last sync will come back and need to be deleted again.',
558
+ 'session.memorySync.wayOut.unrecognized': 'The deployment named a way out that this client does not recognize, so none is suggested here.',
550
559
  'session.hostSettings.memorySwitchMalformed': 'The memory setting in your settings file could not be read, so turning memory off was not applied: this request was sent with memory available. Fix the memory section (memory.enabled must be true or false) to turn it off.',
551
560
  'session.hostSettings.fileHistoryRestoreConflict': 'Nothing was sent: file checkpointing was turned off in your settings after this request was prepared, but this request also restores files from a checkpoint, and a request cannot do both. To restore the files, turn file checkpointing back on in your settings; otherwise send the request without the file restore.',
552
561
  'session.hostSettings.projectionUnchecked': 'The settings for this request could not be checked before sending, so the request is not sent. Reload your settings and try again.',
@@ -670,6 +679,18 @@ const EN = {
670
679
  'session.steerRefusal.advice': '{happened} — {nextStep}',
671
680
  'session.steerRefusal.hostQueue.queued': 'it stays queued and will be submitted as-is when this turn ends',
672
681
  'session.steerRefusal.hostQueue.dropped': 'it was not kept; {nextStep}',
682
+ 'session.permissionModeSwitch.applied': 'The running turn now uses the {mode} permission mode.',
683
+ 'session.permissionModeSwitch.folded': 'The running turn now uses the {mode} permission mode instead of the one you chose — {reason}.',
684
+ 'session.permissionModeSwitch.reason.entryFloor': 'this deployment does not allow a looser mode than its floor',
685
+ 'session.permissionModeSwitch.reason.disableAutoMode': "this task's settings turn auto mode off",
686
+ 'session.permissionModeSwitch.reason.engine': 'the engine does not allow the chosen mode for this run',
687
+ 'session.permissionModeSwitch.notPersisted': '{line} The switch was not saved with the run: if the run pauses and later resumes, it resumes in the mode it had before, so switch again after it resumes.',
688
+ 'session.permissionModeSwitch.notRunning': 'This run is not running a turn right now (it is paused waiting on a decision, has finished, or is running elsewhere), so its mode was not switched.',
689
+ 'session.permissionModeSwitch.notFound': 'No such run is visible to you, so no mode was switched.',
690
+ 'session.permissionModeSwitch.refused': 'The engine refused to switch the mode of this run ({status}), so it keeps the mode it had.',
691
+ 'session.permissionModeSwitch.refusedWithCode': 'The engine refused to switch the mode of this run ({status} {code}), so it keeps the mode it had.',
692
+ 'session.permissionModeSwitch.unconfirmed': 'The switch was sent but no readable answer came back, so whether the mode changed is not known — switch again to be sure.',
693
+ 'session.permissionModeSwitch.unavailable': 'Switching the mode of a running turn is not available here, so the mode was not changed.',
673
694
  'notice.capability.subject.userInvokedSkills': 'user-invoked skills',
674
695
  'notice.capability.subject.skillToolLists': 'skill tool lists',
675
696
  'notice.capability.subject.permissionRulesWrite': 'permission-rule write entry',
@@ -1053,5 +1074,34 @@ const EN = {
1053
1074
  'config.mods.refusal.by': 'Refused by {by}',
1054
1075
  'config.mods.rewrite.resultInPlace': 'Answered by {by} in place of the tool',
1055
1076
  'config.mods.rewrite.inputBy': 'Arguments changed by {by} before the call was let through',
1077
+ 'follow.notFollowed.headRun': 'This screen is no longer following run {taskId}{reason}{ending}{exit}',
1078
+ 'follow.notFollowed.headTurn': "This screen is no longer following this turn's run{reason}{ending}{exit}",
1079
+ 'follow.notFollowed.reason.exhaustedDraining': ": the engine instance serving it was going down when its last event stream closed, and this client's re-attach allowance has run out.",
1080
+ 'follow.notFollowed.reason.exhaustedConnectionLost': ": the connection dropped on the last attempt, and this client's re-attach allowance has run out.",
1081
+ 'follow.notFollowed.reason.exhaustedStreamCap': ": its event stream hit the engine's time limit on the last attempt, and this client's re-attach allowance has run out.",
1082
+ 'follow.notFollowed.reason.exhausted': ": this client's re-attach allowance has run out.",
1083
+ 'follow.notFollowed.reason.badInput': ': this client hit an internal error while re-attaching.',
1084
+ 'follow.notFollowed.reason.noRunId': ': this client never learned which run it was, so there is nothing to re-attach to.',
1085
+ 'follow.notFollowed.reason.runUnknown': ': the engine answered that it does not know that run — it may have been cleaned up, or this client asked with the wrong identity.',
1086
+ 'follow.notFollowed.reason.unspecified': '.',
1087
+ 'follow.notFollowed.endingUnknown': ' The engine has not said how that run ended — it may still be going.',
1088
+ 'follow.notFollowed.exit.reattach': ' What it does next shows up here only after this client re-attaches to it.',
1089
+ 'follow.notFollowed.exit.checkRuns': " Check this session's runs before sending the work again.",
1090
+ 'follow.notFollowed.exit.runUnknown': " Re-attaching will not bring it back here; check this session's runs before sending the work again.",
1091
+ 'follow.notFollowed.idleTear': "This turn's event stream went silent for {secs}s. {notice} {grace}",
1092
+ 'follow.notFollowed.idleGrace': 'If the run legitimately goes quiet this long (e.g. a long build), raising the idle grace may help next time: set {env} (settings env, integer ms, {min}-{max}; default {default}).',
1093
+ 'approval.planReview.note.empty': 'The plan is empty.',
1094
+ 'approval.planReview.note.marked': 'This plan contains characters that cannot be shown as written, so it cannot be approved from this card. Reject it, or ask for the plan to be presented again.',
1095
+ 'approval.planReview.note.tooLarge': 'This plan is too large to be shown in full ({length} characters), so it cannot be approved from this card. Reject it, or ask for a shorter plan.',
1096
+ 'approval.planReview.note.notCarried': "The engine did not bring this plan's text up to this card (it may be too long), so it cannot be approved from this card. Reject it, or ask for the plan to be presented again.",
1097
+ 'approval.planReview.note.unknown': 'This card cannot show the plan it would approve, so it cannot be approved from this card. Reject it, or ask for the plan to be presented again.',
1098
+ 'approval.planReview.askAgain.shown': 'The user asked for the plan to be presented again.',
1099
+ 'approval.planReview.askAgain.marked': 'The user could not approve this plan: it contains characters that cannot be shown as written. Present the plan again without those characters.',
1100
+ 'approval.planReview.askAgain.tooLarge': 'The user could not approve this plan: it is too large to be shown in full. Present a shorter plan.',
1101
+ 'approval.planReview.askAgain.notCarried': 'The user could not approve this plan: its text did not reach the approval card (it may be too long). Present a shorter plan.',
1102
+ 'approval.planReview.askAgain.unknown': 'The user could not approve this plan: the approval card could not show it. Present the plan again.',
1103
+ 'approval.planReview.askAgain.withNote': '{sentence} The user added: {note}',
1104
+ 'approval.planReview.option.askAgain.label': 'No, ask for the plan to be presented again',
1105
+ 'approval.planReview.option.askAgain.description': 'Do not approve; ask the model to present the plan again',
1056
1106
  };
1057
1107
  export const EN_MESSAGES = Object.freeze(EN);