@sema-agent/client-core 0.105.0 → 0.107.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 (152) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/README.md +40 -21
  3. package/dist/adapt/arms.js +10 -1
  4. package/dist/adapter/activeRunSelfHeal.d.ts +21 -2
  5. package/dist/adapter/activeRunSelfHeal.js +129 -28
  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 +29 -0
  19. package/dist/checkpointRowVerdict.js +98 -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/displayUntrusted.d.ts +5 -3
  29. package/dist/displayUntrusted.js +50 -41
  30. package/dist/engineAddress.d.ts +3 -0
  31. package/dist/engineAddress.js +25 -0
  32. package/dist/engineDirectory.js +9 -17
  33. package/dist/engineErrorCodes.d.ts +8 -0
  34. package/dist/engineErrorCodes.js +8 -0
  35. package/dist/engineNoticeCodes.d.ts +38 -3
  36. package/dist/engineNoticeCodes.js +167 -11
  37. package/dist/engineRowRef.js +1 -1
  38. package/dist/errorSupplementRoles.js +4 -1
  39. package/dist/eventLeg.d.ts +2 -2
  40. package/dist/eventLeg.js +2 -1
  41. package/dist/executionLaneCapability.js +3 -2
  42. package/dist/executionLaneWords.d.ts +1 -0
  43. package/dist/executionLaneWords.js +3 -0
  44. package/dist/fleet/fleetProjection.js +26 -5
  45. package/dist/generated/engineFactTables.d.ts +2 -1
  46. package/dist/generated/engineFactTables.js +3 -1
  47. package/dist/generated/engineNoticeTables.js +28 -7
  48. package/dist/generated/sdkFactTables.d.ts +3 -0
  49. package/dist/generated/sdkFactTables.js +15 -0
  50. package/dist/generated/settingsSchemaFns.d.ts +11 -0
  51. package/dist/generated/settingsSchemaFns.js +2 -1
  52. package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
  53. package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
  54. package/dist/generated/settingsSchemaSettingsSources.js +22 -2
  55. package/dist/hitl/approvalLiveFace.d.ts +17 -0
  56. package/dist/hitl/approvalLiveFace.js +35 -0
  57. package/dist/hitl/approvalQueueRow.d.ts +21 -0
  58. package/dist/hitl/approvalQueueRow.js +69 -0
  59. package/dist/hitl/approvalResolution.d.ts +5 -1
  60. package/dist/hitl/approvalResolution.js +18 -1
  61. package/dist/hitl/approvalsFeed.d.ts +5 -4
  62. package/dist/hitl/approvalsFeed.js +39 -6
  63. package/dist/hitl/askParkRowRouting.d.ts +4 -3
  64. package/dist/hitl/askParkRowRouting.js +27 -3
  65. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  66. package/dist/hitl/crashConverged.d.ts +5 -1
  67. package/dist/hitl/decisionConflict.js +2 -1
  68. package/dist/hitl/decisionRefusal.js +1 -1
  69. package/dist/hitl/decisionSend.d.ts +3 -0
  70. package/dist/hitl/decisionSend.js +32 -6
  71. package/dist/hitl/frameRouter.js +4 -5
  72. package/dist/hitl/hitlBridge.d.ts +6 -2
  73. package/dist/hitl/hitlBridge.js +53 -7
  74. package/dist/hitl/livePendingAsk.d.ts +5 -0
  75. package/dist/hitl/livePendingAsk.js +10 -3
  76. package/dist/hitl/parkOwnership.js +4 -5
  77. package/dist/hitl/parkResolver.js +9 -5
  78. package/dist/hitl/parkRowBirthWait.d.ts +7 -0
  79. package/dist/hitl/parkRowBirthWait.js +1 -1
  80. package/dist/hitl/parkRowDelegation.d.ts +6 -0
  81. package/dist/hitl/parkRowDelegation.js +33 -0
  82. package/dist/hitl/persistedRulesWire.d.ts +15 -3
  83. package/dist/hitl/persistedRulesWire.js +102 -20
  84. package/dist/hitl/planReviewCard.d.ts +2 -0
  85. package/dist/hitl/planReviewCard.js +21 -0
  86. package/dist/hitl/planReviewWire.d.ts +2 -2
  87. package/dist/hitl/planReviewWire.js +105 -38
  88. package/dist/hitl/sessionAllowRoute.d.ts +7 -0
  89. package/dist/hitl/sessionAllowRoute.js +49 -0
  90. package/dist/hitl/sessionPolicyWire.d.ts +1 -7
  91. package/dist/hitl/sessionPolicyWire.js +3 -43
  92. package/dist/hitl/standingRows.js +10 -10
  93. package/dist/hitl/subagentSource.d.ts +1 -0
  94. package/dist/hitl/subagentSource.js +13 -0
  95. package/dist/hitl/toolApprovalWire.d.ts +3 -2
  96. package/dist/hitl/toolApprovalWire.js +100 -44
  97. package/dist/hostSettingsSource.d.ts +4 -1
  98. package/dist/hostSettingsSource.js +11 -6
  99. package/dist/hostTurnSettings.d.ts +4 -5
  100. package/dist/hostTurnSettings.js +80 -19
  101. package/dist/i18n/messages.en.d.ts +41 -12
  102. package/dist/i18n/messages.en.js +42 -13
  103. package/dist/i18n/messages.zh.js +45 -13
  104. package/dist/index.d.ts +22 -2
  105. package/dist/index.js +12 -2
  106. package/dist/injectionLaneNotice.js +2 -1
  107. package/dist/liveInitToolFace.js +0 -1
  108. package/dist/liveModelCatalog.d.ts +15 -0
  109. package/dist/liveModelCatalog.js +40 -0
  110. package/dist/mcpWireCaps.js +1 -1
  111. package/dist/modelCapabilityProbe.js +66 -33
  112. package/dist/noticePresentation.js +2 -0
  113. package/dist/ownKey.d.ts +0 -1
  114. package/dist/ownKey.js +0 -2
  115. package/dist/permissionsDeclaration.d.ts +2 -2
  116. package/dist/permissionsDeclaration.js +12 -14
  117. package/dist/projectSeed.d.ts +39 -0
  118. package/dist/projectSeed.js +80 -0
  119. package/dist/promptDelivery.js +1 -1
  120. package/dist/replayBody.d.ts +7 -6
  121. package/dist/replayBody.js +62 -7
  122. package/dist/request/taskRequest.js +3 -4
  123. package/dist/ruleCompileVerdict.d.ts +0 -2
  124. package/dist/ruleCompileVerdict.js +0 -31
  125. package/dist/ruleRefusalSentence.js +0 -3
  126. package/dist/runReattachPolicy.d.ts +3 -0
  127. package/dist/runReattachPolicy.js +15 -2
  128. package/dist/runTerminal.js +30 -8
  129. package/dist/sandboxWire.d.ts +0 -3
  130. package/dist/sandboxWire.js +0 -9
  131. package/dist/sdkWireTransit.d.ts +1 -1
  132. package/dist/seam.d.ts +14 -7
  133. package/dist/seam.js +12 -0
  134. package/dist/seatContract.d.ts +58 -7
  135. package/dist/seatContract.js +135 -4
  136. package/dist/serverSupportRange.d.ts +18 -0
  137. package/dist/serverSupportRange.js +25 -0
  138. package/dist/sessionEngine.d.ts +156 -0
  139. package/dist/sessionEngine.js +464 -0
  140. package/dist/sessionMap.d.ts +11 -1
  141. package/dist/sessionMap.js +35 -9
  142. package/dist/sessionMemorySync.d.ts +31 -0
  143. package/dist/sessionMemorySync.js +95 -0
  144. package/dist/sessionRowTag.d.ts +4 -0
  145. package/dist/sessionRowTag.js +18 -0
  146. package/dist/settingsLayerAdmission.d.ts +1 -0
  147. package/dist/settingsLayerAdmission.js +4 -1
  148. package/dist/toolResult.js +1 -1
  149. package/dist/wireRefusalCopy.d.ts +6 -1
  150. package/dist/wireRefusalCopy.js +9 -7
  151. package/docs/INTEGRATION-CLIENTS.md +1393 -18
  152. package/package.json +5 -5
@@ -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 } : {}),
@@ -122,6 +122,11 @@ declare const EN: {
122
122
  'session.devices.probeFailedTransport': string;
123
123
  'session.devices.probeFailedRefused': string;
124
124
  'session.devices.probeFailedUnreadable': string;
125
+ 'session.engine.selfNotLocal': string;
126
+ 'session.engine.dispatch.notWired': string;
127
+ 'session.engine.dispatch.scopeMismatch': string;
128
+ 'session.engine.dispatch.scopeUnproven': string;
129
+ 'session.engine.dispatch.unresolved': string;
125
130
  'config.cloudLogin.accessDenied': string;
126
131
  'config.cloudLogin.expiredToken': string;
127
132
  'config.cloudLogin.invalidGrant': string;
@@ -141,6 +146,7 @@ declare const EN: {
141
146
  'config.engineTarget.loginFailed': string;
142
147
  'config.engineTarget.storageUnavailable': string;
143
148
  'config.engineTarget.noLocalRuntime': string;
149
+ 'config.engineTarget.serverBelowSupportFloor': string;
144
150
  'config.engineTarget.input': string;
145
151
  'config.engineTarget.unknown': string;
146
152
  'onboard.probe.endpointReachable': string;
@@ -240,6 +246,11 @@ declare const EN: {
240
246
  'approval.settle.cause.broadcastLost': string;
241
247
  'approval.settle.cause.streamDisconnected': string;
242
248
  'approval.settle.cause.undeliverable': string;
249
+ 'approval.decide.requiresBinding': string;
250
+ 'approval.decide.unboundWayOut': string;
251
+ 'approval.rowUnresumable.fallback': string;
252
+ 'approval.rowUnresumable.askNotSurfaced': string;
253
+ 'approval.park.stillParked': string;
243
254
  'approval.decision.grantChanged': string;
244
255
  'approval.decision.settledElsewhereContrary.deniedFirst': string;
245
256
  'approval.decision.settledElsewhereContrary.approvedFirst': string;
@@ -295,6 +306,13 @@ declare const EN: {
295
306
  'session.crashConverged.cause.neutral': string;
296
307
  'session.crashConverged.cause.withClause': string;
297
308
  'session.corrupt.content': string;
309
+ 'session.projectSeed.invalid': string;
310
+ 'session.projectSeed.invalidAt': string;
311
+ 'session.projectSeed.laneNotIsolated': string;
312
+ 'session.projectSeed.seedFixed': string;
313
+ 'session.projectSeed.boundElsewhere': string;
314
+ 'session.projectSeed.laneUnknown': string;
315
+ 'session.projectSeed.missing': string;
298
316
  'resume.reopen.first.envFailed': string;
299
317
  'resume.reopen.first.toolUnavailable': string;
300
318
  'resume.reopen.first.toolContractMismatch': string;
@@ -394,6 +412,8 @@ declare const EN: {
394
412
  'session.selfHeal.injected.handedOffWatch': string;
395
413
  'session.selfHeal.injected.interruptedFinishing': string;
396
414
  'session.selfHeal.injected.notDelivered': string;
415
+ 'session.selfHeal.injected.redundant': string;
416
+ 'session.selfHeal.injected.deferred': string;
397
417
  'session.selfHeal.decisionInFlight': string;
398
418
  'session.selfHeal.parkedOn.ask': string;
399
419
  'session.selfHeal.parkedOn.plan': string;
@@ -406,6 +426,7 @@ declare const EN: {
406
426
  'session.selfHeal.row.askDecided.inferred': string;
407
427
  'session.selfHeal.row.askDecided.releaseUnknown': string;
408
428
  'session.selfHeal.row.askReopenFailed.withKeep': string;
429
+ 'session.selfHeal.row.askReopenFailed.rowUnresumable': string;
409
430
  'session.selfHeal.row.askReopenFailed.noKeep': string;
410
431
  'session.selfHeal.row.planReopenFailed.dismissed': string;
411
432
  'session.selfHeal.row.planReopenFailed.withKeep': string;
@@ -545,6 +566,11 @@ declare const EN: {
545
566
  'approval.boundGrants.denyPart.one': string;
546
567
  'approval.boundGrants.denyPart.other': string;
547
568
  'approval.boundGrants.activates': string;
569
+ 'approval.boundGrants.allowPart.named.one': string;
570
+ 'approval.boundGrants.allowPart.named.other': string;
571
+ 'approval.boundGrants.denyPart.named.one': string;
572
+ 'approval.boundGrants.denyPart.named.other': string;
573
+ 'approval.boundGrants.more': string;
548
574
  'session.fileHistoryRefusal.notYet': string;
549
575
  'session.fileHistoryRefusal.notSaid': string;
550
576
  'session.fileHistoryRefusal.unrecognized': string;
@@ -556,6 +582,12 @@ declare const EN: {
556
582
  'session.memorySync.parkedUnnamed.other': string;
557
583
  'session.memorySync.wayOut.deleteCursor': string;
558
584
  'session.memorySync.wayOut.unrecognized': string;
585
+ 'session.memorySyncTrigger.notConfigured': string;
586
+ 'session.memorySyncTrigger.noSettledRun': string;
587
+ 'session.memorySyncTrigger.runUnobserved': string;
588
+ 'session.memorySyncTrigger.memoryNotMounted': string;
589
+ 'session.memorySyncTrigger.noLeaf': string;
590
+ 'session.memorySyncTrigger.unobtainable': string;
559
591
  'session.hostSettings.memorySwitchMalformed': string;
560
592
  'session.hostSettings.fileHistoryRestoreConflict': string;
561
593
  'session.hostSettings.projectionUnchecked': string;
@@ -565,11 +597,14 @@ declare const EN: {
565
597
  'session.hostSettings.layerNotLoaded.hold.unreadable': string;
566
598
  'session.hostSettings.layerNotLoaded.hold.rejected': string;
567
599
  'session.hostSettings.layerNotLoaded.sent.unreadable': string;
600
+ 'session.hostSettings.layerNotLoaded.restrictions.hold': string;
601
+ 'session.hostSettings.layerNotLoaded.restrictions.sent': string;
568
602
  'session.hostSettings.layer.policySettings': string;
569
603
  'session.hostSettings.layer.userSettings': string;
570
604
  'session.hostSettings.layer.projectSettings': string;
571
605
  'session.hostSettings.layer.localSettings': string;
572
606
  'session.hostSettings.layer.flagSettings': string;
607
+ 'session.hostSettings.layer.foreignProjectSettings': string;
573
608
  'session.hostSettings.heldPrevious': string;
574
609
  'session.hostSettings.styleMissing': string;
575
610
  'session.hostSettings.styleSafeMode': string;
@@ -603,7 +638,6 @@ declare const EN: {
603
638
  'rules.refusal.list.more': string;
604
639
  'rules.refusal.reason.toolName': string;
605
640
  'rules.refusal.reason.homePath': string;
606
- 'rules.refusal.reason.neverMatches': string;
607
641
  'rules.nearMiss.sentence.one': string;
608
642
  'rules.nearMiss.sentence.other': string;
609
643
  'rules.nearMiss.reason': string;
@@ -734,17 +768,6 @@ declare const EN: {
734
768
  'notice.capability.hands.unknownRosterUnreadable': string;
735
769
  'notice.capability.hands.unknownMountConditionAbsent': string;
736
770
  'rules.sessionPolicy.tighten.written': string;
737
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.counts': string;
738
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.unreadable': string;
739
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.unseen': string;
740
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.one': string;
741
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.many': string;
742
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.one': string;
743
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.many': string;
744
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.one': string;
745
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.many': string;
746
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.two': string;
747
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.three': string;
748
771
  'rules.sessionPolicy.tighten.conflict': string;
749
772
  'rules.sessionPolicy.tighten.loosenForbidden': string;
750
773
  'rules.sessionPolicy.tighten.capabilityAbsent': string;
@@ -965,6 +988,7 @@ declare const EN: {
965
988
  'notice.wireRefusal.hostPosture.both': string;
966
989
  'notice.wireRefusal.hostPosture.unattributed': string;
967
990
  'notice.wireRefusal.autonomyLane': string;
991
+ 'notice.wireRefusal.sessionArchiveStamped': string;
968
992
  'notice.wireRefusal.unsupportedRule': string;
969
993
  'notice.wireRefusal.unsupportedRule.other': string;
970
994
  'notice.wireRefusal.unsupportedRule.stored': string;
@@ -1101,6 +1125,11 @@ declare const EN: {
1101
1125
  'approval.planReview.askAgain.notCarried': string;
1102
1126
  'approval.planReview.askAgain.unknown': string;
1103
1127
  'approval.planReview.askAgain.withNote': string;
1128
+ 'approval.planReview.decision.notSent': string;
1129
+ 'approval.planReview.decision.refused': string;
1130
+ 'approval.planReview.decision.cardMissing': string;
1131
+ 'approval.planReview.decision.gateMoved': string;
1132
+ 'approval.planReview.decision.gateUntrusted': string;
1104
1133
  'approval.planReview.option.askAgain.label': string;
1105
1134
  'approval.planReview.option.askAgain.description': string;
1106
1135
  };
@@ -122,6 +122,11 @@ const EN = {
122
122
  'session.devices.probeFailedTransport': "Couldn't reach the engine to check for a remote-control lane — try again",
123
123
  'session.devices.probeFailedRefused': 'The engine refused the device-list check — try again',
124
124
  'session.devices.probeFailedUnreadable': "The engine's device list couldn't be read — try again",
125
+ 'session.engine.selfNotLocal': "This session runs on the engine this app started on this device, but it was started as a Cloud session. Nothing was changed — check how this session was opened.",
126
+ 'session.engine.dispatch.notWired': "This session's engine isn't connected right now, so nothing was sent. Connect to that engine, then try again.",
127
+ 'session.engine.dispatch.scopeMismatch': "This session belongs to an engine in a different space from the one you're signed in to, so nothing was sent. Switch to that space, then try again.",
128
+ 'session.engine.dispatch.scopeUnproven': "Your current sign-in can't be confirmed to reach this session's engine, so nothing was sent. Sign in to that engine's space (or connect to the engine directly), then try again.",
129
+ 'session.engine.dispatch.unresolved': "This session's engine isn't known yet, so nothing was sent. Choose the engine for this session once, then try again.",
125
130
  'config.cloudLogin.accessDenied': 'Sign-in was declined.',
126
131
  'config.cloudLogin.expiredToken': 'The sign-in code expired before it was approved — start again.',
127
132
  'config.cloudLogin.invalidGrant': 'The Cloud sign-in can no longer be renewed — sign in again.',
@@ -141,6 +146,7 @@ const EN = {
141
146
  'config.engineTarget.loginFailed': 'Sign-in failed.',
142
147
  'config.engineTarget.storageUnavailable': "Secure storage isn't available on this device, so the Cloud credential can't be kept. Nothing was changed.",
143
148
  'config.engineTarget.noLocalRuntime': "There's no local engine on this device.",
149
+ 'config.engineTarget.serverBelowSupportFloor': 'This server reports version {version}, older than {floor} — the oldest server this client supports. Requests to it may be refused or not behave as described; connect to a server at {floor} or newer.',
144
150
  'config.engineTarget.input': "The request wasn't in the expected shape, so nothing was changed.",
145
151
  'config.engineTarget.unknown': "The engine switch didn't go through.",
146
152
  'onboard.probe.endpointReachable': '{method} {path} → {status} (endpoint reachable)',
@@ -240,6 +246,11 @@ const EN = {
240
246
  'approval.settle.cause.broadcastLost': 'it never reached any open connection',
241
247
  'approval.settle.cause.streamDisconnected': 'the connection carrying this card dropped',
242
248
  'approval.settle.cause.undeliverable': 'it could not be delivered to any approval surface',
249
+ 'approval.decide.requiresBinding': 'This decision was sent without the binding that ties it to the exact action on the approval card, so the engine refused it · Nothing was decided and the approval is still pending · Fetch the pending approvals again and decide from the card as it is listed now; if the listed card itself carries no binding, {wayOut}',
250
+ 'approval.decide.unboundWayOut': 'it cannot be decided here — cancel the run or let the approval expire',
251
+ 'approval.rowUnresumable.fallback': 'this engine does not report that it can continue this pending approval, so it was not shown as a card that can be approved; nothing was sent',
252
+ 'approval.rowUnresumable.askNotSurfaced': 'the pending row (task {taskId}) cannot be continued by this engine, so it is not surfaced as a card that can be answered: {sentence}',
253
+ 'approval.park.stillParked': '{gate} gate could not be answered: {reason} — the run is still parked on this approval.',
243
254
  'approval.decision.grantChanged': 'The engine refused this approval because the skill grant it was bound to has changed since the card was shown · Nothing was decided and the request is still waiting, but approving it again will be refused the same way · Deny this request so the model can activate the skill again',
244
255
  'approval.decision.settledElsewhereContrary.deniedFirst': 'This request was already answered elsewhere and denied, so your approval did not take effect.',
245
256
  'approval.decision.settledElsewhereContrary.approvedFirst': 'This request was already answered elsewhere and approved, so your denial did not take effect.',
@@ -295,6 +306,13 @@ const EN = {
295
306
  'session.crashConverged.cause.neutral': 'the decision converged automatically',
296
307
  'session.crashConverged.cause.withClause': '{clause}, and the decision converged automatically',
297
308
  'session.corrupt.content': "This session's saved history cannot be read — its stored log is damaged — so the engine cannot open this session. Retrying will not help: whoever operates this engine has to repair or remove that stored log. Until then, continue in a new session.",
309
+ 'session.projectSeed.invalid': 'The project package was refused: it is not a usable project archive. It has to be a gzip tar of the project folder within the size and entry limits, holding only regular files, folders and links that stay inside the folder, each path once, and nothing named .git or attachments at the top level. Fix the folder and upload it again.',
310
+ 'session.projectSeed.invalidAt': '{sentence} Problem entry: {path}',
311
+ 'session.projectSeed.laneNotIsolated': 'The execution lane for this session is not a sandbox, so a project package cannot become its workspace. Hand off a project with a pushed git commit instead, or run the session on a sandboxed lane.',
312
+ 'session.projectSeed.seedFixed': 'A session can start from only one project package: this session already has one, or this submission carried more than one. Send a single package, or start a new session to begin from a different one.',
313
+ 'session.projectSeed.boundElsewhere': 'This upload is already bound to another session, and an upload cannot be moved to a different session, so this submission was not accepted. Upload the project package again for this session.',
314
+ 'session.projectSeed.laneUnknown': 'The engine has not said whether it runs sessions in a sandbox, so a project package cannot be offered as the workspace yet.',
315
+ 'session.projectSeed.missing': 'Upload the project package before handing the session off.',
298
316
  'resume.reopen.first.envFailed': 'The execution environment for this parked work could not be brought back',
299
317
  'resume.reopen.first.toolUnavailable': 'The tool this approval was granted for is not available on this engine',
300
318
  'resume.reopen.first.toolContractMismatch': 'The tool this approval was granted for has changed since the approval was recorded',
@@ -394,6 +412,8 @@ const EN = {
394
412
  'session.selfHeal.injected.handedOffWatch': '{lead} was passed to the reply already in progress{id} instead of starting a new one — watch that reply for what it does with it.',
395
413
  'session.selfHeal.injected.interruptedFinishing': '{lead} was not delivered: the earlier reply{id} that was stopped is still finishing, so sema did not send it and did not retry — the model has not seen it. Give it a moment, then try again.',
396
414
  'session.selfHeal.injected.notDelivered': '{lead} was not delivered: this session was still busy with an earlier reply{id}, and there was no open card you could answer to free it{noView}. sema did not retry, so the model has not seen it.',
415
+ 'session.selfHeal.injected.redundant': '{lead} was not passed on: the reply it was about{id} resumed on its own after that decision, so it has nothing to add.',
416
+ 'session.selfHeal.injected.deferred': '{lead} is waiting: this session is busy with an earlier reply{id}, so sema kept the message and will send it as a new message once that reply has finished or paused.',
397
417
  'session.selfHeal.decisionInFlight': 'The previous turn is parked {parkedOn} and your answer to that card is still on its way to the engine, so sema did not show the card again — answering it a second time would send a second decision. It did NOT cancel the run. Your message was NOT sent; send it again in a moment, once the engine has applied that decision.',
398
418
  'session.selfHeal.parkedOn.ask': 'waiting for your decision (run {taskId})',
399
419
  'session.selfHeal.parkedOn.plan': 'on a plan review (run {taskId})',
@@ -406,6 +426,7 @@ const EN = {
406
426
  'session.selfHeal.row.askDecided.inferred': 'The previous turn was parked on an approval that your permission rules decided on their own (no card was needed); run {taskId} resumed and has since finished — sema is re-sending your message; if that run has not fully let go of the session yet, you may have to send it once more.',
407
427
  'session.selfHeal.row.askDecided.releaseUnknown': 'The previous turn was parked on an approval that your permission rules decided on their own (no card was needed), so run {taskId} should be resuming{status}. sema could not yet confirm it finished, so your message was not sent — wait a moment and send it again.',
408
428
  'session.selfHeal.row.askReopenFailed.withKeep': 'The previous turn is parked waiting for your decision (run {taskId}) and sema could not reopen that card here{noView}. It did NOT cancel the run — that would have decided it for you. Your message was NOT sent. To keep this conversation, {keep}: the card comes back when the session resumes, if it is still waiting. Or {wayOut} if you no longer want it.{viaEngine}',
429
+ 'session.selfHeal.row.askReopenFailed.rowUnresumable': 'The previous turn is parked waiting for your decision (run {taskId}), but this engine cannot continue that pending request, so there is no card to reopen: {sentence} · {unbound}. It did NOT cancel the run. Your message was NOT sent.',
409
430
  'session.selfHeal.row.askReopenFailed.noKeep': 'The previous turn is parked waiting for your decision (run {taskId}) and sema could not reopen that card here{noView}. It did NOT cancel the run — that would have decided it for you. Your message was NOT sent; {wayOut} if you no longer want it.{viaEngine}',
410
431
  'session.selfHeal.row.planReopenFailed.dismissed': 'The previous turn is parked waiting for a plan review (run {taskId}), and you closed its approval card — {closed}. It did NOT cancel the run — that would have discarded the plan for you. Your message was NOT sent; send it again to bring the card back.',
411
432
  'session.selfHeal.row.planReopenFailed.withKeep': 'The previous turn is parked waiting for a plan review (run {taskId}) and sema could not reopen that approval card{noView}. It did NOT cancel the run — that would have discarded the plan for you. Your message was NOT sent. To keep this conversation, {keep}: the card comes back when the session resumes, if it is still waiting. Or {wayOut} if you no longer want that plan.{viaEngine}',
@@ -545,6 +566,11 @@ const EN = {
545
566
  'approval.boundGrants.denyPart.one': '1 tool entry is refused for the rest of this run',
546
567
  'approval.boundGrants.denyPart.other': '{count} tool entries are refused for the rest of this run',
547
568
  'approval.boundGrants.activates': 'Approving this activates the skill shown on the card: {allow}, and {deny}.',
569
+ 'approval.boundGrants.allowPart.named.one': '1 tool entry it lists runs without asking once it is active ({names})',
570
+ 'approval.boundGrants.allowPart.named.other': '{count} tool entries it lists run without asking once it is active ({names})',
571
+ 'approval.boundGrants.denyPart.named.one': '1 tool entry is refused for the rest of this run ({names})',
572
+ 'approval.boundGrants.denyPart.named.other': '{count} tool entries are refused for the rest of this run ({names})',
573
+ 'approval.boundGrants.more': 'and {count} more',
548
574
  'session.fileHistoryRefusal.notYet': "Nothing was sent: file checkpointing is turned off in your settings, but this engine's capabilities have not been read yet, so whether it can stop recording file history for a session is unknown and sending now could record it anyway. Try again in a moment; to send regardless and let file history be recorded this time, turn file checkpointing back on in your settings.",
549
575
  '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.',
550
576
  '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.',
@@ -556,6 +582,12 @@ const EN = {
556
582
  'session.memorySync.parkedUnnamed.other': 'Memory sync is paused on this deployment for {count} layers: their sync cursors could not be used. {wayOut}',
557
583
  '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
584
  'session.memorySync.wayOut.unrecognized': 'The deployment named a way out that this client does not recognize, so none is suggested here.',
585
+ 'session.memorySyncTrigger.notConfigured': 'Nothing to sync: this session does not use any memory.',
586
+ 'session.memorySyncTrigger.noSettledRun': 'Memory cannot be synced yet: no run in this session has finished with a record of which memory it reads. Run a task in this session (or let the one in progress finish), then sync again.',
587
+ 'session.memorySyncTrigger.runUnobserved': 'Memory cannot be synced: the latest finished run in this session has no record of which memory it read, and an earlier run is never used in its place. Run another task in this session (if that run was refused, fix what refused it first), then sync again.',
588
+ 'session.memorySyncTrigger.memoryNotMounted': 'Memory cannot be synced: memory was set up for the latest finished run in this session but did not load, so which memory this session reads is unknown. Run another task in this session (if its memory fails to load again, fix that first), then sync again.',
589
+ 'session.memorySyncTrigger.noLeaf': 'Memory cannot be synced yet: this session has no conversation yet, so there is nothing to sync from. Run a task in it (or let the one in progress finish), then sync again.',
590
+ 'session.memorySyncTrigger.unobtainable': 'Memory cannot be synced: which memory this session reads could not be determined. Run a task in this session (or let the one in progress finish), then sync again.',
559
591
  '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.',
560
592
  '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.',
561
593
  '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.',
@@ -565,11 +597,14 @@ const EN = {
565
597
  'session.hostSettings.layerNotLoaded.hold.unreadable': '{layer} could not be read, so the rules and hooks in that file would not apply to this request. Continue without these settings, or cancel and fix the file first.',
566
598
  'session.hostSettings.layerNotLoaded.hold.rejected': '{layer} were not loaded because a hook guarding tool use in that file is malformed, so the rules and hooks in that file would not apply to this request. Continue without these settings, or cancel and fix the file first.',
567
599
  'session.hostSettings.layerNotLoaded.sent.unreadable': '{layer} could not be read: this request was sent without the rules and hooks in that file.',
600
+ 'session.hostSettings.layerNotLoaded.restrictions.hold': '{layer} could not be read, so the restrictions in that file (deny and ask rules) would not apply to this request. Continue without them, or cancel and fix the file first.',
601
+ 'session.hostSettings.layerNotLoaded.restrictions.sent': '{layer} could not be read: this request was sent without the restrictions in that file (deny and ask rules).',
568
602
  'session.hostSettings.layer.policySettings': 'Managed settings',
569
603
  'session.hostSettings.layer.userSettings': 'Your user settings',
570
604
  'session.hostSettings.layer.projectSettings': 'The project settings',
571
605
  'session.hostSettings.layer.localSettings': 'The local project settings',
572
606
  'session.hostSettings.layer.flagSettings': 'The settings passed at launch',
607
+ 'session.hostSettings.layer.foreignProjectSettings': "The project's settings for another tool",
573
608
  'session.hostSettings.heldPrevious': 'A settings file could not be read cleanly just now (it may be in the middle of being saved), so the permission rules from the last clean read are still applied.',
574
609
  'session.hostSettings.styleMissing': 'The output style "{name}" selected in your settings could not be found or read, so it is not applied to this request.',
575
610
  'session.hostSettings.styleSafeMode': 'Safe mode is on, so the output style from your settings is not applied.',
@@ -603,7 +638,6 @@ const EN = {
603
638
  'rules.refusal.list.more': '{shown} and {count} more',
604
639
  'rules.refusal.reason.toolName': 'its tool name is not one this engine recognizes',
605
640
  'rules.refusal.reason.homePath': 'it uses a ~/ path this engine cannot anchor — write the path relative to the project or as an absolute path',
606
- 'rules.refusal.reason.neverMatches': 'this rule form cannot match as a deny or ask rule for this tool, so the engine would never apply it — to block the tool altogether, write its name alone',
607
641
  'rules.nearMiss.sentence.one': 'permissions: 1 rule names a tool by a spelling no tool of this run answers to, so it can never match — {list}',
608
642
  'rules.nearMiss.sentence.other': 'permissions: {count} rules name a tool by a spelling no tool of this run answers to, so they can never match — {list}',
609
643
  'rules.nearMiss.reason': 'write the name as "{mounted}"',
@@ -734,17 +768,6 @@ const EN = {
734
768
  'notice.capability.hands.unknownRosterUnreadable': 'whether this run has file-and-shell tools is unknown — the tool roster could not be read in the shape this reader knows',
735
769
  'notice.capability.hands.unknownMountConditionAbsent': 'whether this run has file-and-shell tools is unknown — this engine does not report which condition mounted each tool, so an empty count here would be a fact about the reader, not about the run',
736
770
  'rules.sessionPolicy.tighten.written': 'Session rules saved (version {rev}) — the record now holds everything it already had plus the entries sent here, and it applies from this session’s next tool gate onward; the step already running is not interrupted.',
737
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.counts': "Still running, and possibly started before this session's first rule: {list}. Anything that started before that first rule keeps running without this one — to make the rule apply to it now, stop it.",
738
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.unreadable': "This client could not read which background agents or workflows are running now. Anything that started before this session's first rule keeps running without this one — stop whatever must not keep running without it.",
739
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.unseen': 'Work whose start has not reached this client yet may not read this rule either.',
740
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.one': '{n} background agent',
741
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.many': '{n} background agents',
742
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.one': '{n} workflow',
743
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.many': '{n} workflows',
744
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.one': '{n} background item this client stopped tracking',
745
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.many': '{n} background items this client stopped tracking',
746
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.two': '{a} and {b}',
747
- 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.three': '{a}, {b} and {c}',
748
771
  'rules.sessionPolicy.tighten.conflict': 'Session rules were changed by someone else while this tightening was being written, twice in a row — nothing was written. Re-read the current rules and try again, or let the other writer finish first.',
749
772
  'rules.sessionPolicy.tighten.loosenForbidden': 'The engine refused this write because it would relax the session rules: at least one entry sent here widens what is allowed rather than narrowing it. Only entries that add restrictions are accepted on this path.',
750
773
  'rules.sessionPolicy.tighten.capabilityAbsent': 'This deployment does not keep per-session tool rules, so there is nothing to tighten here.',
@@ -965,6 +988,7 @@ const EN = {
965
988
  'notice.wireRefusal.hostPosture.both': "The engine refused this request before anything ran: the request asked both for the shell to inherit this machine's whole environment and for bypassPermissions, and this deployment refused at least one of them; the refusal does not say which · {notLocal}; send it again without one or both (the whole-environment setting is honored only on a single-user deployment)",
966
989
  'notice.wireRefusal.hostPosture.unattributed': "The engine refused this request before anything ran: this deployment does not accept a setting in it that only a single-user deployment may use; the engine's own message names it · {notLocal}; send it again without that setting",
967
990
  'notice.wireRefusal.autonomyLane': 'The engine did not start this leader run: this deployment runs in plan-only autonomy, and a leader run has no way to have its plan reviewed, so nothing was started · Submit the work as a regular run instead, or ask whoever operates the engine to change its autonomy setting',
991
+ 'notice.wireRefusal.sessionArchiveStamped': "This session's stored archive still carries an owner stamp written by an earlier server version, so it cannot be forked or have its log replaced; nothing was written · Delete the session and push it again",
968
992
  'notice.wireRefusal.unsupportedRule': 'The engine refused this request, so none of it took effect: a rule in it is a form the engine does not support — {detail} · Remove or rewrite that rule (to stop a tool altogether, write its name alone), then send it again',
969
993
  'notice.wireRefusal.unsupportedRule.other': 'The engine refused this request, so none of it took effect: {count} rules in it are forms the engine does not support — {detail} · Remove or rewrite those rules (to stop a tool altogether, write its name alone), then send it again',
970
994
  'notice.wireRefusal.unsupportedRule.stored': 'The engine refused to continue this work: the request stored for it has a rule in it that is a form the engine does not support — {detail} · It cannot continue as stored. Remove or rewrite that rule (to stop a tool altogether, write its name alone), cancel the work if it is still suspended, then submit it again as a new request',
@@ -1078,7 +1102,7 @@ const EN = {
1078
1102
  'follow.notFollowed.headTurn': "This screen is no longer following this turn's run{reason}{ending}{exit}",
1079
1103
  '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
1104
  '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.",
1105
+ 'follow.notFollowed.reason.exhaustedStreamCap': ": the engine ended its event stream with a stream-limit notice on the last attempt, and this client's re-attach allowance has run out.",
1082
1106
  'follow.notFollowed.reason.exhausted': ": this client's re-attach allowance has run out.",
1083
1107
  'follow.notFollowed.reason.badInput': ': this client hit an internal error while re-attaching.',
1084
1108
  'follow.notFollowed.reason.noRunId': ': this client never learned which run it was, so there is nothing to re-attach to.',
@@ -1101,6 +1125,11 @@ const EN = {
1101
1125
  '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
1126
  'approval.planReview.askAgain.unknown': 'The user could not approve this plan: the approval card could not show it. Present the plan again.',
1103
1127
  'approval.planReview.askAgain.withNote': '{sentence} The user added: {note}',
1128
+ 'approval.planReview.decision.notSent': 'The plan_review {decision} could NOT be sent; the plan is still waiting for a decision.',
1129
+ 'approval.planReview.decision.refused': 'This {decision} was not applied: the engine refused it and changed nothing.',
1130
+ 'approval.planReview.decision.cardMissing': 'Approving a plan needs the card that showed it, and none came with this approval.',
1131
+ 'approval.planReview.decision.gateMoved': 'The plan on this card is no longer the one waiting for approval.',
1132
+ 'approval.planReview.decision.gateUntrusted': "The engine could not recognise this review's gate id (the server's record of it looks damaged; an operator should check it). The card still shows the plan that is waiting: answering again sends the answer without the gate id, and that answer is not protected against the plan having changed.",
1104
1133
  'approval.planReview.option.askAgain.label': 'No, ask for the plan to be presented again',
1105
1134
  'approval.planReview.option.askAgain.description': 'Do not approve; ask the model to present the plan again',
1106
1135
  };